diff --git a/.env.example b/.env.example index d401e5b..acbe41f 100644 --- a/.env.example +++ b/.env.example @@ -30,3 +30,6 @@ SENTINEL_CLIENT_ID= SENTINEL_CLIENT_SECRET= SENTINEL_WORKSPACE_ID= MUSTER_MOCK_INTEGRATIONS=true +MUSTER_AGENT_GATEWAY_TOKEN=replace-with-at-least-32-random-bytes +# Optional comma-separated HTTPS origins for additional Alfie research feeds. +MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS= diff --git a/README.md b/README.md index 227752e..7155219 100644 --- a/README.md +++ b/README.md @@ -177,8 +177,10 @@ that will use the service. When using HTTPS behind a reverse proxy, set `AUTH_SECURE_COOKIES=true`; do not add broad wildcard origins. The installer creates `.env.homelab` with mode `600`. Keep it out of source -control. The generated topology still uses synthetic connector endpoints until -you deliberately configure governed real connectors. +control. Its generated `MUSTER_AGENT_GATEWAY_TOKEN` authenticates internal +web/worker calls to the unexposed agent gateway; do not reuse or publish it. +The generated topology still uses synthetic connector endpoints until you +deliberately configure governed real connectors. ### Codex subscription authentication diff --git a/apps/agent-gateway/src/index.ts b/apps/agent-gateway/src/index.ts index 9762b29..3a5778b 100644 --- a/apps/agent-gateway/src/index.ts +++ b/apps/agent-gateway/src/index.ts @@ -15,6 +15,10 @@ import { import { and, eq } from "drizzle-orm"; import { z } from "zod"; import { DurableAgentRuntime } from "./runtime.ts"; +import { + isGatewayRequestAuthorised, + parseGatewayOrganisationId, +} from "./service-auth.ts"; const AgentRunRequestSchema = AgentInvestigationJobSchema.extend({ humanRequest: z.string().trim().min(1).max(4_000).optional(), @@ -24,6 +28,10 @@ const executionRuntime = process.env.MUSTER_AGENT_RUNTIME === "mock" ? "mock" : "codex"; const codexHome = process.env.CODEX_HOME ?? "/var/lib/muster/codex"; const globalKillSwitch = process.env.AGENT_KILL_SWITCH === "true"; +const gatewayToken = z + .string() + .min(32) + .parse(process.env.MUSTER_AGENT_GATEWAY_TOKEN); const runtime = new DurableAgentRuntime({ executionRuntime, codexHome, @@ -47,6 +55,12 @@ async function body(request: IncomingMessage): Promise { return JSON.parse(Buffer.concat(chunks).toString("utf8")); } +function requestOrganisationId(request: IncomingMessage) { + return parseGatewayOrganisationId( + request.headers["x-muster-organisation-id"], + ); +} + async function queueDirectRun( input: z.infer, idempotencyKey: string, @@ -170,12 +184,26 @@ const server = createServer(async (incoming, response) => { return; } + if ( + !isGatewayRequestAuthorised(incoming.headers.authorization, gatewayToken) + ) { + response.writeHead(401); + response.end(JSON.stringify({ error: "Unauthorised" })); + return; + } + const runMatch = incoming.method === "GET" ? url.pathname.match(/^\/v1\/runs\/([^/]+)$/) : null; if (runMatch?.[1]) { - const run = await runtime.read(runMatch[1]); + const organisationId = requestOrganisationId(incoming); + if (!organisationId) { + response.writeHead(400); + response.end(JSON.stringify({ error: "Organisation header required" })); + return; + } + const run = await runtime.read(runMatch[1], organisationId); response.writeHead(run ? 200 : 404); response.end(JSON.stringify(run ?? { error: "Run not found" })); return; @@ -234,6 +262,12 @@ const server = createServer(async (incoming, response) => { ); return; } + const organisationId = requestOrganisationId(incoming); + if (!organisationId || organisationId !== parsed.data.organisationId) { + response.writeHead(403); + response.end(JSON.stringify({ error: "Organisation mismatch" })); + return; + } const idempotencyKey = incoming.headers["idempotency-key"]?.toString().trim() || `api:${parsed.data.traceId}`; @@ -269,7 +303,13 @@ const server = createServer(async (incoming, response) => { ? url.pathname.match(/^\/v1\/runs\/([^/]+)\/cancel$/) : null; if (cancelMatch?.[1]) { - const cancelled = await runtime.cancel(cancelMatch[1]); + const organisationId = requestOrganisationId(incoming); + if (!organisationId) { + response.writeHead(400); + response.end(JSON.stringify({ error: "Organisation header required" })); + return; + } + const cancelled = await runtime.cancel(cancelMatch[1], organisationId); response.writeHead(cancelled ? 202 : 404); response.end( JSON.stringify({ diff --git a/apps/agent-gateway/src/runtime.integration.test.ts b/apps/agent-gateway/src/runtime.integration.test.ts index aeea457..8a4766b 100644 --- a/apps/agent-gateway/src/runtime.integration.test.ts +++ b/apps/agent-gateway/src/runtime.integration.test.ts @@ -18,13 +18,10 @@ const describeIntegration = integration ? describe.sequential : describe.skip; describe("Codex structured output schema", () => { it("removes unsupported URI formats while preserving authoritative validation", () => { - const generated = z.toJSONSchema( - AgentStructuredOutputSchemas.HuntResult, - { - target: "draft-2020-12", - io: "output", - }, - ); + const generated = z.toJSONSchema(AgentStructuredOutputSchemas.HuntResult, { + target: "draft-2020-12", + io: "output", + }); expect(JSON.stringify(generated)).toContain('"format":"uri"'); expect(JSON.stringify(codexOutputSchemaFor("HuntResult"))).not.toContain( '"format":"uri"', @@ -172,6 +169,42 @@ describeIntegration("durable agent runtime", () => { throw new Error(`Run ${runId} did not reach ${status}`); } + async function directMessageSource(suffix: string) { + const [room] = await database() + .select({ id: schema.rooms.id }) + .from(schema.rooms) + .innerJoin( + schema.roomMemberships, + and( + eq(schema.roomMemberships.organisationId, organisationId), + eq(schema.roomMemberships.roomId, schema.rooms.id), + eq(schema.roomMemberships.actorId, agentId), + ), + ) + .where( + and( + eq(schema.rooms.organisationId, organisationId), + eq(schema.rooms.roomType, "direct"), + ), + ) + .limit(1); + if (!room) throw new Error("Seeded agent direct room required"); + const messageId = newId(); + await database() + .insert(schema.messages) + .values({ + id: messageId, + organisationId, + roomId: room.id, + authorActorId: requestedByActorId, + messageType: "text", + document: { type: "doc", content: [] }, + plainText: `Synthetic direct request ${suffix}`, + idempotencyKey: `runtime-direct-source:${messageId}`, + }); + return { messageId, roomId: room.id }; + } + it("recovers an expired lease without duplicating the run", async () => { const run = await insertRun("restart"); const duplicateId = newId(); @@ -229,7 +262,11 @@ describeIntegration("durable agent runtime", () => { codexHome: "/tmp/muster-runtime-integration", }); expect( - await runtime.cancel(run.id, "Synthetic operator cancellation"), + await runtime.cancel( + run.id, + organisationId, + "Synthetic operator cancellation", + ), ).toBe(true); const cancelled = await waitFor(run.id, "cancelled"); expect(cancelled.cancellationRequestedAt).not.toBeNull(); @@ -238,6 +275,22 @@ describeIntegration("durable agent runtime", () => { ); }); + it("scopes run reads and cancellations by organisation", async () => { + const run = await insertRun("cross-organisation-guard"); + const runtime = new DurableAgentRuntime({ + executionRuntime: "mock", + codexHome: "/tmp/muster-runtime-integration", + }); + const otherOrganisationId = newId(); + + await expect(runtime.read(run.id, otherOrganisationId)).resolves.toBeNull(); + await expect(runtime.cancel(run.id, otherOrganisationId)).resolves.toBe( + false, + ); + const persisted = await waitFor(run.id, "queued"); + expect(persisted.organisationId).toBe(organisationId); + }); + it("returns a redacted observer projection without changing the execution record", async () => { const canary = `synthetic-api-secret-${newId()}`; const run = await insertRun("redacted-observer", { @@ -269,7 +322,7 @@ describeIntegration("durable agent runtime", () => { codexHome: "/tmp/muster-runtime-integration", }); - const projection = await runtime.read(run.id); + const projection = await runtime.read(run.id, organisationId); const serialisedProjection = JSON.stringify(projection); expect(serialisedProjection).not.toContain(canary); expect(serialisedProjection).toContain("[REDACTED]"); @@ -577,4 +630,310 @@ describeIntegration("durable agent runtime", () => { }); expect(message?.relatedAgentRunId).toBe(run.id); }); + + it("projects a completed direct-message run as one linked room reply", async () => { + const source = await directMessageSource("completed"); + const run = await insertRun("direct-completed", { + roomId: source.roomId, + request: { + kind: "direct_message", + sourceMessageId: source.messageId, + humanRequest: "Review the synthetic direct request", + traceId: `integration-direct-${source.messageId}`, + }, + }); + const runtime = new DurableAgentRuntime({ + executionRuntime: "mock", + codexHome: "/tmp/muster-runtime-integration", + mockDelayMs: 10, + }); + await runtime.dispatch(); + await waitFor(run.id, "completed"); + runtime.stop(); + + const [replies, outbox] = await Promise.all([ + database() + .select() + .from(schema.messages) + .where( + eq( + schema.messages.idempotencyKey, + `agent-direct-message-reply:${run.id}`, + ), + ), + database() + .select() + .from(schema.outboxEvents) + .where( + eq( + schema.outboxEvents.idempotencyKey, + `room.message.created:agent-direct-message:${run.id}`, + ), + ), + ]); + expect(replies).toHaveLength(1); + expect(outbox).toHaveLength(1); + const reply = replies[0]; + expect(reply).toMatchObject({ + roomId: source.roomId, + threadParentId: source.messageId, + authorActorId: agentId, + messageType: "agent-status", + relatedAgentRunId: run.id, + }); + expect(reply?.document).toMatchObject({ + type: "agent-direct-message-reply", + status: "completed", + sourceMessageId: source.messageId, + agentRunId: run.id, + trust: "agent-analysis", + }); + expect(outbox[0]?.aggregateId).toBe(reply?.id); + }); + + it("projects a failed direct-message run as one linked room reply", async () => { + const source = await directMessageSource("failed"); + const run = await insertRun("direct-failed", { + roomId: source.roomId, + maximumTokenBudget: 1, + request: { + kind: "direct_message", + sourceMessageId: source.messageId, + humanRequest: "Review the synthetic direct request", + traceId: `integration-direct-${source.messageId}`, + }, + }); + const runtime = new DurableAgentRuntime({ + executionRuntime: "mock", + codexHome: "/tmp/muster-runtime-integration", + mockDelayMs: 10, + }); + await runtime.dispatch(); + await waitFor(run.id, "failed"); + runtime.stop(); + + const replies = await database() + .select() + .from(schema.messages) + .where( + eq( + schema.messages.idempotencyKey, + `agent-direct-message-reply:${run.id}`, + ), + ); + expect(replies).toHaveLength(1); + expect(replies[0]).toMatchObject({ + roomId: source.roomId, + threadParentId: source.messageId, + authorActorId: agentId, + messageType: "agent-status", + relatedAgentRunId: run.id, + }); + expect(replies[0]?.document).toMatchObject({ + type: "agent-direct-message-reply", + status: "failed", + sourceMessageId: source.messageId, + agentRunId: run.id, + failureCode: "token_ceiling", + }); + const outbox = await database() + .select() + .from(schema.outboxEvents) + .where( + eq( + schema.outboxEvents.idempotencyKey, + `room.message.created:agent-direct-message:${run.id}`, + ), + ); + expect(outbox).toHaveLength(1); + }); + + it("projects a direct-message kill-switch failure before execution", async () => { + const source = await directMessageSource("kill-switch"); + const run = await insertRun("direct-kill-switch", { + roomId: source.roomId, + request: { + kind: "direct_message", + sourceMessageId: source.messageId, + humanRequest: "Review the synthetic direct request", + traceId: `integration-direct-${source.messageId}`, + }, + }); + await database() + .update(schema.agentDefinitions) + .set({ killSwitch: true }) + .where(eq(schema.agentDefinitions.id, agentId)); + const runtime = new DurableAgentRuntime({ + executionRuntime: "mock", + codexHome: "/tmp/muster-runtime-integration", + mockDelayMs: 10, + }); + try { + await runtime.dispatch(); + await waitFor(run.id, "failed"); + } finally { + runtime.stop(); + await database() + .update(schema.agentDefinitions) + .set({ killSwitch: false }) + .where(eq(schema.agentDefinitions.id, agentId)); + } + + const [reply] = await database() + .select() + .from(schema.messages) + .where( + eq( + schema.messages.idempotencyKey, + `agent-direct-message-reply:${run.id}`, + ), + ); + expect(reply).toMatchObject({ + threadParentId: source.messageId, + messageType: "agent-status", + relatedAgentRunId: run.id, + }); + expect(reply?.document).toMatchObject({ + status: "failed", + failureCode: "agent_kill_switch", + sourceMessageId: source.messageId, + agentRunId: run.id, + }); + }); + + it("projects a cancelled direct-message run exactly once", async () => { + const source = await directMessageSource("cancelled"); + const run = await insertRun("direct-cancelled", { + roomId: source.roomId, + request: { + kind: "direct_message", + sourceMessageId: source.messageId, + humanRequest: "Review the synthetic direct request", + traceId: `integration-direct-${source.messageId}`, + }, + }); + const runtime = new DurableAgentRuntime({ + executionRuntime: "mock", + codexHome: "/tmp/muster-runtime-integration", + mockDelayMs: 10, + }); + expect( + await runtime.cancel( + run.id, + organisationId, + "Synthetic operator cancellation", + ), + ).toBe(true); + await waitFor(run.id, "cancelled"); + runtime.stop(); + + const [replies, outbox] = await Promise.all([ + database() + .select() + .from(schema.messages) + .where( + eq( + schema.messages.idempotencyKey, + `agent-direct-message-reply:${run.id}`, + ), + ), + database() + .select() + .from(schema.outboxEvents) + .where( + eq( + schema.outboxEvents.idempotencyKey, + `room.message.created:agent-direct-message:${run.id}`, + ), + ), + ]); + expect(replies).toHaveLength(1); + expect(replies[0]).toMatchObject({ + roomId: source.roomId, + threadParentId: source.messageId, + authorActorId: agentId, + messageType: "agent-status", + relatedAgentRunId: run.id, + }); + expect(replies[0]?.document).toMatchObject({ + status: "cancelled", + failureCode: "operator_cancelled", + sourceMessageId: source.messageId, + agentRunId: run.id, + }); + expect(outbox).toHaveLength(1); + }); + + it("fails queued direct messages when room authorisation is revoked", async () => { + const source = await directMessageSource("revoked-room"); + const run = await insertRun("direct-revoked-room", { + roomId: source.roomId, + request: { + kind: "direct_message", + sourceMessageId: source.messageId, + humanRequest: "Review the synthetic direct request", + traceId: `integration-direct-${source.messageId}`, + }, + }); + const [definition] = await database() + .select({ allowedRooms: schema.agentDefinitions.allowedRooms }) + .from(schema.agentDefinitions) + .where(eq(schema.agentDefinitions.id, agentId)) + .limit(1); + await database() + .update(schema.agentDefinitions) + .set({ allowedRooms: [] }) + .where(eq(schema.agentDefinitions.id, agentId)); + const runtime = new DurableAgentRuntime({ + executionRuntime: "mock", + codexHome: "/tmp/muster-runtime-integration", + mockDelayMs: 10, + }); + try { + await runtime.dispatch(); + const failed = await waitFor(run.id, "failed"); + expect(failed.failureCode).toBe("direct_message_not_authorised"); + expect(failed.startedAt).toBeNull(); + } finally { + runtime.stop(); + await database() + .update(schema.agentDefinitions) + .set({ allowedRooms: definition?.allowedRooms ?? [] }) + .where(eq(schema.agentDefinitions.id, agentId)); + } + }); + + it("reports inactive agents truthfully before execution", async () => { + const source = await directMessageSource("inactive"); + const run = await insertRun("direct-inactive", { + roomId: source.roomId, + request: { + kind: "direct_message", + sourceMessageId: source.messageId, + humanRequest: "Review the synthetic direct request", + traceId: `integration-direct-${source.messageId}`, + }, + }); + await database() + .update(schema.agentDefinitions) + .set({ status: "inactive" }) + .where(eq(schema.agentDefinitions.id, agentId)); + const runtime = new DurableAgentRuntime({ + executionRuntime: "mock", + codexHome: "/tmp/muster-runtime-integration", + mockDelayMs: 10, + }); + try { + await runtime.dispatch(); + const failed = await waitFor(run.id, "failed"); + expect(failed.failureCode).toBe("agent_inactive"); + expect(failed.startedAt).toBeNull(); + } finally { + runtime.stop(); + await database() + .update(schema.agentDefinitions) + .set({ status: "active" }) + .where(eq(schema.agentDefinitions.id, agentId)); + } + }); }); diff --git a/apps/agent-gateway/src/runtime.ts b/apps/agent-gateway/src/runtime.ts index 63c48b7..86f287b 100644 --- a/apps/agent-gateway/src/runtime.ts +++ b/apps/agent-gateway/src/runtime.ts @@ -27,20 +27,158 @@ import { TenantRepository, writeOutbox, } from "@muster/database"; -import { and, asc, eq, inArray, isNull, lt, or, sql } from "drizzle-orm"; +import { and, asc, eq, gt, inArray, isNull, lt, or, sql } from "drizzle-orm"; import { z } from "zod"; type AgentRunRow = typeof schema.agentRuns.$inferSelect; type Context = Awaited>; +type Db = ReturnType; +type Tx = Parameters[0]>[0]; type PersistedRequest = { - kind?: "jessie_hunt" | undefined; + kind?: "jessie_hunt" | "direct_message" | undefined; huntId?: string | undefined; huntPlan?: unknown; humanRequest?: string | undefined; + sourceMessageId?: string | undefined; traceId?: string | undefined; }; +function terminalSummary(output: unknown) { + if (!output || typeof output !== "object" || Array.isArray(output)) + return "The agent completed the request with schema-valid output."; + const record = output as Record; + for (const key of ["summary", "narrative", "headline", "title"]) { + const value = record[key]; + if (typeof value === "string" && value.trim()) + return value.trim().slice(0, 10_000); + } + return "The agent completed the request with schema-valid output."; +} + +async function projectDirectMessageTerminalReply( + tx: Tx, + run: AgentRunRow, + request: PersistedRequest, + terminal: + | { + status: "completed"; + output: unknown; + outputHash: string; + outputSchema: AgentStructuredOutputName; + } + | { + status: "failed"; + failureCode: string; + error: string; + } + | { + status: "cancelled"; + failureCode: string; + error: string; + }, +) { + if ( + request.kind !== "direct_message" || + !request.sourceMessageId || + !run.roomId + ) + return; + const [source] = await tx + .select({ id: schema.messages.id }) + .from(schema.messages) + .innerJoin( + schema.rooms, + and( + eq(schema.rooms.organisationId, run.organisationId), + eq(schema.rooms.id, run.roomId), + eq(schema.rooms.roomType, "direct"), + isNull(schema.rooms.archivedAt), + ), + ) + .innerJoin( + schema.roomMemberships, + and( + eq(schema.roomMemberships.organisationId, run.organisationId), + eq(schema.roomMemberships.roomId, run.roomId), + eq(schema.roomMemberships.actorId, run.agentId), + or( + isNull(schema.roomMemberships.accessExpiresAt), + gt(schema.roomMemberships.accessExpiresAt, new Date()), + ), + ), + ) + .where( + and( + eq(schema.messages.organisationId, run.organisationId), + eq(schema.messages.id, request.sourceMessageId), + eq(schema.messages.roomId, run.roomId), + isNull(schema.messages.deletedAt), + ), + ) + .limit(1); + if (!source) return; + + const completed = terminal.status === "completed"; + const plainText = completed + ? terminalSummary(terminal.output) + : terminal.status === "cancelled" + ? `The agent request was cancelled (${terminal.failureCode}).` + : `The agent could not complete this request (${terminal.failureCode}). Retry the request or contact an operator if the problem continues.`; + const messageId = newId(); + const [message] = await tx + .insert(schema.messages) + .values({ + id: messageId, + organisationId: run.organisationId, + roomId: run.roomId, + threadParentId: request.sourceMessageId, + authorActorId: run.agentId, + messageType: "agent-status", + document: completed + ? { + type: "agent-direct-message-reply", + status: terminal.status, + sourceMessageId: request.sourceMessageId, + agentRunId: run.id, + outputSchema: terminal.outputSchema, + outputHash: terminal.outputHash, + summary: plainText, + trust: "agent-analysis", + } + : { + type: "agent-direct-message-reply", + status: terminal.status, + sourceMessageId: request.sourceMessageId, + agentRunId: run.id, + failureCode: terminal.failureCode, + }, + plainText, + dataClassification: "internal", + relatedInvestigationId: run.investigationId, + relatedAgentRunId: run.id, + idempotencyKey: `agent-direct-message-reply:${run.id}`, + }) + .onConflictDoNothing() + .returning({ id: schema.messages.id }); + if (!message) return; + await writeOutbox(tx, { + organisationId: run.organisationId, + eventType: "room.message.created", + aggregateType: "message", + aggregateId: message.id, + queueName: "muster-outbox", + payload: { + messageId: message.id, + roomId: run.roomId, + threadParentId: request.sourceMessageId, + agentRunId: run.id, + }, + idempotencyKey: `room.message.created:agent-direct-message:${run.id}`, + traceId: redactObservationText(request.traceId ?? `agent-run-${run.id}`), + }); +} + function removeUnsupportedCodexSchemaFormats(value: unknown): unknown { if (Array.isArray(value)) return value.map(removeUnsupportedCodexSchemaFormats); @@ -319,12 +457,17 @@ export class DurableAgentRuntime { this.lastReadinessSnapshotAt = now.getTime(); } - async read(runId: string) { + async read(runId: string, organisationId: string) { const db = database(); const [run] = await db .select() .from(schema.agentRuns) - .where(eq(schema.agentRuns.id, runId)) + .where( + and( + eq(schema.agentRuns.organisationId, organisationId), + eq(schema.agentRuns.id, runId), + ), + ) .limit(1); if (!run) return null; const events = await db @@ -360,7 +503,11 @@ export class DurableAgentRuntime { return redactForObservation(projection) as typeof projection; } - async cancel(runId: string, reason = "Cancelled by operator") { + async cancel( + runId: string, + organisationId: string, + reason = "Cancelled by operator", + ) { const now = new Date(); const [run] = await database().transaction(async (tx) => { const [updated] = await tx @@ -376,6 +523,7 @@ export class DurableAgentRuntime { }) .where( and( + eq(schema.agentRuns.organisationId, organisationId), eq(schema.agentRuns.id, runId), or( eq(schema.agentRuns.status, "awaiting_approval"), @@ -407,6 +555,16 @@ export class DurableAgentRuntime { this.request(updated).traceId ?? `agent-run-${updated.id}`, ), }); + await projectDirectMessageTerminalReply( + tx, + updated, + this.request(updated), + { + status: "cancelled", + failureCode: "operator_cancelled", + error: reason, + }, + ); const [hunt] = await tx .update(schema.huntRuns) .set({ @@ -505,8 +663,20 @@ export class DurableAgentRuntime { const recovered = candidate.status === "running"; const [run] = await database().transaction(async (tx) => { const [definition] = await tx - .select({ killSwitch: schema.agentDefinitions.killSwitch }) + .select({ + status: schema.agentDefinitions.status, + killSwitch: schema.agentDefinitions.killSwitch, + allowedRooms: schema.agentDefinitions.allowedRooms, + actorStatus: schema.actors.status, + }) .from(schema.agentDefinitions) + .leftJoin( + schema.actors, + and( + eq(schema.actors.organisationId, candidate.organisationId), + eq(schema.actors.id, schema.agentDefinitions.id), + ), + ) .where( and( eq(schema.agentDefinitions.id, candidate.agentId), @@ -517,14 +687,92 @@ export class DurableAgentRuntime { ), ) .limit(1); - if (!definition || definition.killSwitch) { + const request = this.request(candidate); + let eligibilityFailure: { code: string; message: string } | undefined; + if (!definition) { + eligibilityFailure = { + code: "agent_unavailable", + message: "Agent definition is unavailable", + }; + } else if (definition.killSwitch) { + eligibilityFailure = { + code: "agent_kill_switch", + message: "Agent is disabled by its kill switch", + }; + } else if ( + definition.status !== "active" || + definition.actorStatus !== "active" + ) { + eligibilityFailure = { + code: "agent_inactive", + message: "Agent is inactive", + }; + } else if (request.kind === "direct_message") { + const sourceMessageId = request.sourceMessageId; + const roomId = candidate.roomId; + if ( + !sourceMessageId || + !roomId || + !Array.isArray(definition.allowedRooms) || + !definition.allowedRooms.includes(roomId) + ) { + eligibilityFailure = { + code: "direct_message_not_authorised", + message: "Direct-message room is no longer authorised", + }; + } else { + const [authorisedRoom] = await tx + .select({ id: schema.messages.id }) + .from(schema.messages) + .innerJoin( + schema.rooms, + and( + eq(schema.rooms.organisationId, candidate.organisationId), + eq(schema.rooms.id, roomId), + eq(schema.rooms.roomType, "direct"), + isNull(schema.rooms.archivedAt), + ), + ) + .innerJoin( + schema.roomMemberships, + and( + eq( + schema.roomMemberships.organisationId, + candidate.organisationId, + ), + eq(schema.roomMemberships.roomId, roomId), + eq(schema.roomMemberships.actorId, candidate.agentId), + or( + isNull(schema.roomMemberships.accessExpiresAt), + gt(schema.roomMemberships.accessExpiresAt, now), + ), + ), + ) + .where( + and( + eq(schema.messages.organisationId, candidate.organisationId), + eq(schema.messages.id, sourceMessageId), + eq(schema.messages.roomId, roomId), + isNull(schema.messages.deletedAt), + ), + ) + .limit(1); + if (!authorisedRoom) { + eligibilityFailure = { + code: "direct_message_not_authorised", + message: "Direct-message room is no longer authorised", + }; + } + } + } + if (eligibilityFailure) { const [disabled] = await tx .update(schema.agentRuns) .set({ status: "failed", completedAt: now, - failureCode: "agent_kill_switch", - error: "Agent is disabled by its kill switch", + failureCode: eligibilityFailure.code, + error: eligibilityFailure.message, leaseExpiresAt: null, }) .where( @@ -540,9 +788,31 @@ export class DurableAgentRuntime { organisationId: disabled.organisationId, runId: disabled.id, eventType: "failed", - message: "Agent kill switch blocked execution", - payload: { failureCode: "agent_kill_switch" }, + message: eligibilityFailure.message, + payload: { failureCode: eligibilityFailure.code }, }); + await appendAuditEvent(tx, { + organisationId: disabled.organisationId, + actorId: disabled.agentId, + actorType: "agent", + action: "agent.run.failed", + targetType: "agent_run", + targetId: disabled.id, + metadata: { failureCode: eligibilityFailure.code }, + traceId: redactObservationText( + this.request(disabled).traceId ?? `agent-run-${disabled.id}`, + ), + }); + await projectDirectMessageTerminalReply( + tx, + disabled, + this.request(disabled), + { + status: "failed", + failureCode: eligibilityFailure.code, + error: eligibilityFailure.message, + }, + ); } return []; } @@ -681,7 +951,7 @@ export class DurableAgentRuntime { ), ); } else if (controller.signal.aborted) { - if (!this.stopping) await this.cancel(run.id); + if (!this.stopping) await this.cancel(run.id, run.organisationId); } else { await this.fail( run, @@ -802,8 +1072,10 @@ export class DurableAgentRuntime { evidence: [ { type: "fixture", - reference: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog", - sha256: "0000000000000000000000000000000000000000000000000000000000000000", + reference: + "https://www.cisa.gov/known-exploited-vulnerabilities-catalog", + sha256: + "0000000000000000000000000000000000000000000000000000000000000000", }, ], }, @@ -866,8 +1138,23 @@ export class DurableAgentRuntime { comparisonPeriod: null, }, filters: { organisationScoped: true }, - metricDefinitions: [{ key: "mtta", definition: "Synthetic", population: "Synthetic", exclusions: "Synthetic" }], - values: [{ key: "mtta", value: null, unit: "minutes", state: "unavailable", sampleSize: 0 }], + metricDefinitions: [ + { + key: "mtta", + definition: "Synthetic", + population: "Synthetic", + exclusions: "Synthetic", + }, + ], + values: [ + { + key: "mtta", + value: null, + unit: "minutes", + state: "unavailable", + sampleSize: 0, + }, + ], sourceReferences: [{ source: "synthetic", query: {} }], narrative: base.summary, caveats: ["Synthetic runtime output"], @@ -1092,6 +1379,12 @@ export class DurableAgentRuntime { this.request(run).traceId ?? `agent-run-${run.id}`, ), }); + await projectDirectMessageTerminalReply(tx, run, this.request(run), { + status: "completed", + output: result.output, + outputHash: result.outputHash, + outputSchema: result.schemaName, + }); const [hunt] = await tx .update(schema.huntRuns) .set({ @@ -1265,6 +1558,11 @@ export class DurableAgentRuntime { this.request(run).traceId ?? `agent-run-${run.id}`, ), }); + await projectDirectMessageTerminalReply(tx, run, this.request(run), { + status: "failed", + failureCode: failure.code, + error: failure.message, + }); const [hunt] = await tx .update(schema.huntRuns) .set({ @@ -1327,10 +1625,11 @@ export class DurableAgentRuntime { private request(run: AgentRunRow): PersistedRequest { const parsed = z .object({ - kind: z.literal("jessie_hunt").optional(), + kind: z.enum(["jessie_hunt", "direct_message"]).optional(), huntId: z.uuid().optional(), huntPlan: z.unknown().optional(), humanRequest: z.string().optional(), + sourceMessageId: z.uuid().optional(), traceId: z.string().optional(), }) .safeParse(run.request); diff --git a/apps/agent-gateway/src/service-auth.test.ts b/apps/agent-gateway/src/service-auth.test.ts new file mode 100644 index 0000000..79721f2 --- /dev/null +++ b/apps/agent-gateway/src/service-auth.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; +import { + isGatewayRequestAuthorised, + parseGatewayOrganisationId, +} from "./service-auth.ts"; + +const token = "synthetic-agent-gateway-token-at-least-32-bytes"; + +describe("agent gateway service authentication", () => { + it("accepts only the exact bearer token", () => { + expect(isGatewayRequestAuthorised(`Bearer ${token}`, token)).toBe(true); + expect(isGatewayRequestAuthorised(undefined, token)).toBe(false); + expect(isGatewayRequestAuthorised(`Basic ${token}`, token)).toBe(false); + expect(isGatewayRequestAuthorised(`Bearer ${token}x`, token)).toBe(false); + expect( + isGatewayRequestAuthorised( + "Bearer synthetic-agent-gateway-token-wrong-value", + token, + ), + ).toBe(false); + }); + + it("accepts one valid organisation UUID header", () => { + const organisationId = "019fa127-8566-770b-939a-971ce03829f6"; + expect(parseGatewayOrganisationId(organisationId)).toBe(organisationId); + expect(parseGatewayOrganisationId(undefined)).toBeNull(); + expect(parseGatewayOrganisationId("not-an-id")).toBeNull(); + expect(parseGatewayOrganisationId([organisationId])).toBeNull(); + }); +}); diff --git a/apps/agent-gateway/src/service-auth.ts b/apps/agent-gateway/src/service-auth.ts new file mode 100644 index 0000000..7d8be26 --- /dev/null +++ b/apps/agent-gateway/src/service-auth.ts @@ -0,0 +1,21 @@ +import { timingSafeEqual } from "node:crypto"; +import { z } from "zod"; + +export function isGatewayRequestAuthorised( + authorization: string | undefined, + expectedToken: string, +) { + if (!authorization?.startsWith("Bearer ")) return false; + const supplied = Buffer.from(authorization.slice("Bearer ".length), "utf8"); + const expected = Buffer.from(expectedToken, "utf8"); + return ( + supplied.length === expected.length && timingSafeEqual(supplied, expected) + ); +} + +export function parseGatewayOrganisationId( + value: string | string[] | undefined, +) { + const parsed = z.string().uuid().safeParse(value); + return parsed.success ? parsed.data : null; +} diff --git a/apps/web/app/api/v1/agent-runs/[id]/route.ts b/apps/web/app/api/v1/agent-runs/[id]/route.ts index 75e8fb6..1a845c8 100644 --- a/apps/web/app/api/v1/agent-runs/[id]/route.ts +++ b/apps/web/app/api/v1/agent-runs/[id]/route.ts @@ -8,6 +8,7 @@ import { problemResponse, requestTraceId, } from "@/lib/api-context"; +import { agentGatewayHeaders } from "@/lib/agent-gateway"; import { settleAgentRun, type AgentRunResult } from "@/lib/task-domain"; export async function GET( @@ -35,7 +36,10 @@ export async function GET( } const gateway = await fetch( `${process.env.AGENT_GATEWAY_URL ?? "http://agent-gateway:3002"}/v1/runs/${encodeURIComponent(id)}`, - { signal: AbortSignal.timeout(5_000) }, + { + headers: agentGatewayHeaders(subject.organisationId), + signal: AbortSignal.timeout(5_000), + }, ); const result = (await gateway.json()) as { status?: string; diff --git a/apps/web/app/api/v1/agents/[id]/learning/route.ts b/apps/web/app/api/v1/agents/[id]/learning/route.ts index 4e428b6..9c44869 100644 --- a/apps/web/app/api/v1/agents/[id]/learning/route.ts +++ b/apps/web/app/api/v1/agents/[id]/learning/route.ts @@ -22,7 +22,12 @@ export async function GET( const subject = await apiSubject(request); requireCapability(subject, "agents.read"); const { id } = await params; - const data = await agentLearningState(subject.organisationId, id); + const includeInactive = + new URL(request.url).searchParams.get("includeInactive") === "true"; + if (includeInactive) requireCapability(subject, "agents.manage"); + const data = await agentLearningState(subject.organisationId, id, { + includeInactive, + }); return Response.json({ data, traceId }); } catch (error) { return problemResponse(error, traceId); diff --git a/apps/web/app/api/v1/hunts/route.ts b/apps/web/app/api/v1/hunts/route.ts index 01ba1ed..e454ad9 100644 --- a/apps/web/app/api/v1/hunts/route.ts +++ b/apps/web/app/api/v1/hunts/route.ts @@ -1,6 +1,6 @@ import { requireCapability } from "@muster/authz"; import { database, schema } from "@muster/database"; -import { desc, eq } from "drizzle-orm"; +import { and, desc, eq, isNull } from "drizzle-orm"; import { apiSubject, problemResponse, requestTraceId } from "@/lib/api-context"; import { JessieHuntDomainService } from "@/lib/jessie-hunt-domain"; @@ -12,7 +12,12 @@ export async function GET(request: Request) { const rows = await database() .select() .from(schema.huntRuns) - .where(eq(schema.huntRuns.organisationId, subject.organisationId)) + .where( + and( + eq(schema.huntRuns.organisationId, subject.organisationId), + isNull(schema.huntRuns.archivedAt), + ), + ) .orderBy(desc(schema.huntRuns.createdAt)) .limit(100); return Response.json({ data: rows, traceId }); diff --git a/apps/web/app/api/v1/maintenance/synthetic-cleanup/route.ts b/apps/web/app/api/v1/maintenance/synthetic-cleanup/route.ts new file mode 100644 index 0000000..9dbd574 --- /dev/null +++ b/apps/web/app/api/v1/maintenance/synthetic-cleanup/route.ts @@ -0,0 +1,18 @@ +import { apiSubject, problemResponse, requestTraceId } from "@/lib/api-context"; +import { SyntheticCleanupDomainService } from "@/lib/synthetic-cleanup-domain"; + +export async function POST(request: Request) { + const traceId = requestTraceId(request); + try { + const subject = await apiSubject(request); + const body = await request.json(); + const data = await new SyntheticCleanupDomainService().execute( + subject, + body, + traceId, + ); + return Response.json({ data, traceId }); + } catch (error) { + return problemResponse(error, traceId); + } +} diff --git a/apps/web/app/api/v1/reports/route.ts b/apps/web/app/api/v1/reports/route.ts index fa0b83a..71a3ccd 100644 --- a/apps/web/app/api/v1/reports/route.ts +++ b/apps/web/app/api/v1/reports/route.ts @@ -1,6 +1,6 @@ import { requireCapability } from "@muster/authz"; import { database, schema } from "@muster/database"; -import { and, desc, eq } from "drizzle-orm"; +import { and, desc, eq, isNull } from "drizzle-orm"; import { apiSubject, problemResponse, requestTraceId } from "@/lib/api-context"; import { ParkerReportDomainService } from "@/lib/parker-report-domain"; @@ -9,25 +9,47 @@ export async function GET(request: Request) { try { const subject = await apiSubject(request); requireCapability(subject, "agents.read"); - const data = await database().select({ report: schema.reportManifests }).from(schema.reportManifests) + const data = await database() + .select({ report: schema.reportManifests }) + .from(schema.reportManifests) .innerJoin( schema.roomMemberships, and( - eq(schema.roomMemberships.organisationId, schema.reportManifests.organisationId), + eq( + schema.roomMemberships.organisationId, + schema.reportManifests.organisationId, + ), eq(schema.roomMemberships.roomId, schema.reportManifests.roomId), eq(schema.roomMemberships.actorId, subject.actorId), ), ) - .where(eq(schema.reportManifests.organisationId, subject.organisationId)) - .orderBy(desc(schema.reportManifests.createdAt)).limit(100); + .where( + and( + eq(schema.reportManifests.organisationId, subject.organisationId), + isNull(schema.reportManifests.archivedAt), + ), + ) + .orderBy(desc(schema.reportManifests.createdAt)) + .limit(100); return Response.json({ data: data.map((row) => row.report), traceId }); - } catch (error) { return problemResponse(error, traceId); } + } catch (error) { + return problemResponse(error, traceId); + } } export async function POST(request: Request) { const traceId = requestTraceId(request); try { - const result = await new ParkerReportDomainService().create(await apiSubject(request), await request.json(), traceId); - return Response.json({ data: result, traceId }, { status: result.duplicate ? 200 : 202 }); - } catch (error) { return problemResponse(error, traceId); } + const result = await new ParkerReportDomainService().create( + await apiSubject(request), + await request.json(), + traceId, + ); + return Response.json( + { data: result, traceId }, + { status: result.duplicate ? 200 : 202 }, + ); + } catch (error) { + return problemResponse(error, traceId); + } } diff --git a/apps/web/app/api/v1/reports/schedules/route.ts b/apps/web/app/api/v1/reports/schedules/route.ts index 05b50e8..93518eb 100644 --- a/apps/web/app/api/v1/reports/schedules/route.ts +++ b/apps/web/app/api/v1/reports/schedules/route.ts @@ -1,21 +1,42 @@ import { requireCapability } from "@muster/authz"; import { database, schema } from "@muster/database"; -import { and, desc, eq } from "drizzle-orm"; +import { and, desc, eq, isNull } from "drizzle-orm"; import { apiSubject, problemResponse, requestTraceId } from "@/lib/api-context"; import { ParkerReportDomainService } from "@/lib/parker-report-domain"; export async function GET(request: Request) { const traceId = requestTraceId(request); try { - const subject = await apiSubject(request); requireCapability(subject, "administration.manage"); - const data = await database().select().from(schema.reportSchedules).where(eq(schema.reportSchedules.organisationId, subject.organisationId)).orderBy(desc(schema.reportSchedules.nextRunAt)); + const subject = await apiSubject(request); + requireCapability(subject, "administration.manage"); + const data = await database() + .select() + .from(schema.reportSchedules) + .where( + and( + eq(schema.reportSchedules.organisationId, subject.organisationId), + isNull(schema.reportSchedules.archivedAt), + ), + ) + .orderBy(desc(schema.reportSchedules.nextRunAt)); return Response.json({ data, traceId }); - } catch (error) { return problemResponse(error, traceId); } + } catch (error) { + return problemResponse(error, traceId); + } } export async function POST(request: Request) { const traceId = requestTraceId(request); try { - const result = await new ParkerReportDomainService().createSchedule(await apiSubject(request), await request.json(), traceId); - return Response.json({ data: result, traceId }, { status: result.duplicate ? 200 : 201 }); - } catch (error) { return problemResponse(error, traceId); } + const result = await new ParkerReportDomainService().createSchedule( + await apiSubject(request), + await request.json(), + traceId, + ); + return Response.json( + { data: result, traceId }, + { status: result.duplicate ? 200 : 201 }, + ); + } catch (error) { + return problemResponse(error, traceId); + } } diff --git a/apps/web/app/api/v1/rooms/[id]/messages/route.ts b/apps/web/app/api/v1/rooms/[id]/messages/route.ts index 757e4c5..41b32e1 100644 --- a/apps/web/app/api/v1/rooms/[id]/messages/route.ts +++ b/apps/web/app/api/v1/rooms/[id]/messages/route.ts @@ -4,6 +4,7 @@ import { PostMessageSchema, RoomService } from "@muster/rooms"; import { apiSubject, problemResponse, requestTraceId } from "@/lib/api-context"; import { enforceApiRateLimit } from "@/lib/api-rate-limit"; import { publishRealtime } from "@/lib/realtime"; +import { AgentDirectMessageDomainService } from "@/lib/agent-direct-message-domain"; import { JessieHuntDomainService } from "@/lib/jessie-hunt-domain"; export async function GET( @@ -48,11 +49,28 @@ export async function POST( roomId: id, }); const result = await new RoomService().postMessage(subject, input, traceId); + let agentInvocation: Awaited< + ReturnType + > = null; + let agentInvocationError: string | null = null; let jessieHunt: Awaited< ReturnType > = null; let jessieHuntError: string | null = null; - if (result.created) { + try { + agentInvocation = await new AgentDirectMessageDomainService().maybeQueue( + subject, + { messageId: result.message.id, roomId: id }, + traceId, + ); + } catch (error) { + agentInvocationError = redactObservationText( + error instanceof Error + ? error.message + : "The direct-message agent could not be queued.", + ); + } + if (result.created && !agentInvocation && !agentInvocationError) { try { jessieHunt = await new JessieHuntDomainService().maybeCreateFromMention( subject, @@ -91,6 +109,8 @@ export async function POST( { data: result.message, duplicate: !result.created, + agentInvocation, + agentInvocationError, jessieHunt, jessieHuntError, realtimeDegraded: !realtimeDelivered, diff --git a/apps/web/app/api/v1/tasks/[id]/cancel/route.ts b/apps/web/app/api/v1/tasks/[id]/cancel/route.ts index 3eb1ef6..9d63979 100644 --- a/apps/web/app/api/v1/tasks/[id]/cancel/route.ts +++ b/apps/web/app/api/v1/tasks/[id]/cancel/route.ts @@ -7,6 +7,7 @@ import { problemResponse, requestTraceId, } from "@/lib/api-context"; +import { agentGatewayHeaders } from "@/lib/agent-gateway"; import { settleAgentRun } from "@/lib/task-domain"; export async function POST( @@ -49,6 +50,7 @@ export async function POST( const gateway = await fetch( `${process.env.AGENT_GATEWAY_URL ?? "http://agent-gateway:3002"}/v1/runs/${encodeURIComponent(task.agentRunId)}/cancel`, { + headers: agentGatewayHeaders(subject.organisationId), method: "POST", signal: AbortSignal.timeout(5_000), }, diff --git a/apps/web/app/api/v1/tasks/[id]/events/route.ts b/apps/web/app/api/v1/tasks/[id]/events/route.ts index ac4ca64..77e0288 100644 --- a/apps/web/app/api/v1/tasks/[id]/events/route.ts +++ b/apps/web/app/api/v1/tasks/[id]/events/route.ts @@ -7,6 +7,7 @@ import { problemResponse, requestTraceId, } from "@/lib/api-context"; +import { agentGatewayHeaders } from "@/lib/agent-gateway"; import { settleAgentRun, type AgentRunResult } from "@/lib/task-domain"; export const dynamic = "force-dynamic"; @@ -47,7 +48,10 @@ export async function GET( while (!request.signal.aborted) { const gateway = await fetch( `${process.env.AGENT_GATEWAY_URL ?? "http://agent-gateway:3002"}/v1/runs/${encodeURIComponent(task.agentRunId!)}`, - { signal: AbortSignal.timeout(5_000) }, + { + headers: agentGatewayHeaders(subject.organisationId), + signal: AbortSignal.timeout(5_000), + }, ); const result = (await gateway.json()) as AgentRunResult & { status: "running" | "completed" | "failed" | "cancelled"; diff --git a/apps/web/app/api/v1/tasks/route.ts b/apps/web/app/api/v1/tasks/route.ts index c286b65..09b2123 100644 --- a/apps/web/app/api/v1/tasks/route.ts +++ b/apps/web/app/api/v1/tasks/route.ts @@ -1,4 +1,4 @@ -import { and, asc, eq, inArray } from "drizzle-orm"; +import { and, asc, eq, inArray, isNull } from "drizzle-orm"; import { requireCapability } from "@muster/authz"; import { redactForObservation } from "@muster/config"; import { TaskPrioritySchema, TaskStatusSchema } from "@muster/contracts"; @@ -28,7 +28,12 @@ async function taskView(organisationId: string, includeEvidence: boolean) { const rows = await db .select() .from(schema.tasks) - .where(eq(schema.tasks.organisationId, organisationId)) + .where( + and( + eq(schema.tasks.organisationId, organisationId), + isNull(schema.tasks.archivedAt), + ), + ) .orderBy(asc(schema.tasks.status), asc(schema.tasks.createdAt)); const actorIds = rows .map((task) => task.assignedActorId) diff --git a/apps/web/lib/agent-direct-message-domain.integration.test.ts b/apps/web/lib/agent-direct-message-domain.integration.test.ts new file mode 100644 index 0000000..28aa8ee --- /dev/null +++ b/apps/web/lib/agent-direct-message-domain.integration.test.ts @@ -0,0 +1,253 @@ +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { closeDatabase, database, newId, schema } from "@muster/database"; +import { and, eq } from "drizzle-orm"; +import { AgentDirectMessageDomainService } from "./agent-direct-message-domain"; + +const integration = process.env.MUSTER_INTEGRATION_TESTS === "true"; +const describeIntegration = integration ? describe.sequential : describe.skip; + +describeIntegration("agent direct-message invocation", () => { + let organisationId = ""; + let humanActorId = ""; + let agentId = ""; + let roomId = ""; + let subject: { + actorId: string; + organisationId: string; + capabilities: Set; + }; + + beforeAll(async () => { + const humans = await database() + .select() + .from(schema.actors) + .where(eq(schema.actors.actorType, "human")); + const human = humans.find( + (actor) => + Array.isArray(actor.capabilityAssignments) && + actor.capabilityAssignments.includes("agents.invoke"), + ); + if ( + !human || + !Array.isArray(human.capabilityAssignments) || + !human.capabilityAssignments.includes("agents.invoke") + ) { + throw new Error("Bootstrapped agent invoker required"); + } + organisationId = human.organisationId; + humanActorId = human.id; + subject = { + actorId: human.id, + organisationId, + capabilities: new Set(human.capabilityAssignments as any[]), + }; + agentId = newId(); + roomId = newId(); + await database() + .insert(schema.actors) + .values({ + id: agentId, + organisationId, + actorType: "agent", + displayName: "Synthetic DM Agent", + identityReference: `agent:synthetic-dm:${agentId}`, + capabilityAssignments: [], + }); + await database() + .insert(schema.rooms) + .values({ + id: roomId, + organisationId, + name: `synthetic-dm-${roomId}`, + slug: `synthetic-dm-${roomId}`, + displayName: "Synthetic DM Agent", + roomType: "direct", + visibility: "private", + createdByActorId: humanActorId, + }); + await database() + .insert(schema.roomMemberships) + .values([ + { + organisationId, + roomId, + actorId: humanActorId, + membershipRole: "owner", + }, + { + organisationId, + roomId, + actorId: agentId, + membershipRole: "agent_member", + }, + ]); + await database() + .insert(schema.agentDefinitions) + .values({ + id: agentId, + organisationId, + name: `Synthetic DM Agent ${agentId}`, + description: "Synthetic direct-message integration fixture", + runtime: "mock", + model: "synthetic", + ownerActorId: humanActorId, + systemPromptVersion: "synthetic-dm-v1", + allowedRooms: [roomId], + maximumRuntimeSeconds: 30, + maximumTokenBudget: 1_000, + maximumCostCents: 10, + }); + }); + + afterAll(closeDatabase); + + async function sourceMessage(targetRoomId = roomId) { + const id = newId(); + await database() + .insert(schema.messages) + .values({ + id, + organisationId, + roomId: targetRoomId, + authorActorId: humanActorId, + messageType: "text", + document: { type: "doc", content: [] }, + plainText: `Review synthetic evidence ${id}`, + idempotencyKey: `synthetic-dm-source:${id}`, + }); + return id; + } + + it("queues one durable run, event, audit, and outbox idempotently", async () => { + const messageId = await sourceMessage(); + const service = new AgentDirectMessageDomainService(); + const first = await service.maybeQueue( + subject, + { messageId, roomId }, + `trace-${messageId}`, + ); + const replay = await service.maybeQueue( + subject, + { messageId, roomId }, + `trace-replay-${messageId}`, + ); + + expect(first).toMatchObject({ + handled: true, + queued: true, + duplicate: false, + agentId, + status: "queued", + }); + expect(replay).toMatchObject({ + handled: true, + queued: true, + duplicate: true, + agentRunId: + first && first.queued ? first.agentRunId : "missing-agent-run", + }); + if (!first?.queued) throw new Error("Agent run was not queued"); + const [events, outbox, audit] = await Promise.all([ + database() + .select() + .from(schema.agentRunEvents) + .where(eq(schema.agentRunEvents.runId, first.agentRunId)), + database() + .select() + .from(schema.outboxEvents) + .where( + eq( + schema.outboxEvents.idempotencyKey, + `agent.run.queued:${first.agentRunId}`, + ), + ), + database() + .select() + .from(schema.auditEvents) + .where( + and( + eq(schema.auditEvents.organisationId, organisationId), + eq(schema.auditEvents.targetType, "agent_run"), + eq(schema.auditEvents.targetId, first.agentRunId), + eq(schema.auditEvents.action, "agent.run.queued"), + ), + ), + ]); + expect(events).toHaveLength(1); + expect(outbox).toHaveLength(1); + expect(audit).toHaveLength(1); + }); + + it("requires agents.invoke before queueing", async () => { + const messageId = await sourceMessage(); + await expect( + new AgentDirectMessageDomainService().maybeQueue( + { ...subject, capabilities: new Set() }, + { messageId, roomId }, + `trace-${messageId}`, + ), + ).rejects.toThrow("Missing capability: agents.invoke"); + }); + + it("honours the kill switch and allowed-room boundary", async () => { + const service = new AgentDirectMessageDomainService(); + const disabledMessageId = await sourceMessage(); + await database() + .update(schema.agentDefinitions) + .set({ killSwitch: true }) + .where(eq(schema.agentDefinitions.id, agentId)); + expect( + await service.maybeQueue( + subject, + { messageId: disabledMessageId, roomId }, + `trace-${disabledMessageId}`, + ), + ).toMatchObject({ queued: false, reason: "agent_count" }); + + await database() + .update(schema.agentDefinitions) + .set({ killSwitch: false, allowedRooms: [] }) + .where(eq(schema.agentDefinitions.id, agentId)); + const disallowedMessageId = await sourceMessage(); + expect( + await service.maybeQueue( + subject, + { messageId: disallowedMessageId, roomId }, + `trace-${disallowedMessageId}`, + ), + ).toMatchObject({ queued: false, reason: "agent_unavailable" }); + await database() + .update(schema.agentDefinitions) + .set({ allowedRooms: [roomId] }) + .where(eq(schema.agentDefinitions.id, agentId)); + }); + + it("does not handle a non-direct room", async () => { + const [room] = await database() + .select({ id: schema.rooms.id }) + .from(schema.rooms) + .innerJoin( + schema.roomMemberships, + and( + eq(schema.roomMemberships.roomId, schema.rooms.id), + eq(schema.roomMemberships.actorId, humanActorId), + ), + ) + .where( + and( + eq(schema.rooms.organisationId, organisationId), + eq(schema.rooms.roomType, "operations"), + ), + ) + .limit(1); + if (!room) throw new Error("Bootstrapped operations room required"); + const messageId = await sourceMessage(room.id); + await expect( + new AgentDirectMessageDomainService().maybeQueue( + subject, + { messageId, roomId: room.id }, + `trace-${messageId}`, + ), + ).resolves.toBeNull(); + }); +}); diff --git a/apps/web/lib/agent-direct-message-domain.ts b/apps/web/lib/agent-direct-message-domain.ts new file mode 100644 index 0000000..10b561d --- /dev/null +++ b/apps/web/lib/agent-direct-message-domain.ts @@ -0,0 +1,330 @@ +import { createHash } from "node:crypto"; +import { requireCapability, type AuthorisationSubject } from "@muster/authz"; +import { redactObservationText } from "@muster/config"; +import { + appendAuditEvent, + database, + newId, + schema, + writeOutbox, +} from "@muster/database"; +import { and, eq, gt, isNull, or } from "drizzle-orm"; + +type Db = ReturnType; + +export type DirectMessageInvocation = + | { + handled: true; + queued: false; + duplicate: false; + reason: "agent_count" | "agent_unavailable"; + } + | { + handled: true; + queued: true; + duplicate: boolean; + agentId: string; + agentRunId: string; + status: string; + }; + +export class AgentDirectMessageDomainService { + constructor(private readonly db: Db = database()) {} + + async maybeQueue( + subject: AuthorisationSubject, + input: { messageId: string; roomId: string }, + traceId: string, + ): Promise { + const [source] = await this.db + .select({ + plainText: schema.messages.plainText, + relatedInvestigationId: schema.messages.relatedInvestigationId, + }) + .from(schema.messages) + .innerJoin( + schema.rooms, + and( + eq(schema.rooms.organisationId, subject.organisationId), + eq(schema.rooms.id, schema.messages.roomId), + eq(schema.rooms.roomType, "direct"), + isNull(schema.rooms.archivedAt), + ), + ) + .innerJoin( + schema.roomMemberships, + and( + eq(schema.roomMemberships.organisationId, subject.organisationId), + eq(schema.roomMemberships.roomId, schema.messages.roomId), + eq(schema.roomMemberships.actorId, subject.actorId), + or( + isNull(schema.roomMemberships.accessExpiresAt), + gt(schema.roomMemberships.accessExpiresAt, new Date()), + ), + ), + ) + .innerJoin( + schema.actors, + and( + eq(schema.actors.organisationId, subject.organisationId), + eq(schema.actors.id, schema.messages.authorActorId), + eq(schema.actors.id, subject.actorId), + eq(schema.actors.actorType, "human"), + eq(schema.actors.status, "active"), + ), + ) + .where( + and( + eq(schema.messages.organisationId, subject.organisationId), + eq(schema.messages.id, input.messageId), + eq(schema.messages.roomId, input.roomId), + eq(schema.messages.messageType, "text"), + isNull(schema.messages.deletedAt), + ), + ) + .limit(1); + if (!source) return null; + + const members = await this.db + .select({ + actorId: schema.actors.id, + actorStatus: schema.actors.status, + definitionId: schema.agentDefinitions.id, + definitionStatus: schema.agentDefinitions.status, + killSwitch: schema.agentDefinitions.killSwitch, + allowedRooms: schema.agentDefinitions.allowedRooms, + runtime: schema.agentDefinitions.runtime, + model: schema.agentDefinitions.model, + promptVersion: schema.agentDefinitions.systemPromptVersion, + maximumRuntimeSeconds: schema.agentDefinitions.maximumRuntimeSeconds, + maximumTokenBudget: schema.agentDefinitions.maximumTokenBudget, + maximumCostCents: schema.agentDefinitions.maximumCostCents, + }) + .from(schema.roomMemberships) + .innerJoin( + schema.actors, + and( + eq(schema.actors.organisationId, subject.organisationId), + eq(schema.actors.id, schema.roomMemberships.actorId), + eq(schema.actors.actorType, "agent"), + ), + ) + .leftJoin( + schema.agentDefinitions, + and( + eq(schema.agentDefinitions.organisationId, subject.organisationId), + eq(schema.agentDefinitions.id, schema.actors.id), + ), + ) + .where( + and( + eq(schema.roomMemberships.organisationId, subject.organisationId), + eq(schema.roomMemberships.roomId, input.roomId), + or( + isNull(schema.roomMemberships.accessExpiresAt), + gt(schema.roomMemberships.accessExpiresAt, new Date()), + ), + ), + ); + if (members.length === 0) return null; + + const active = members.filter( + (member) => + member.actorStatus === "active" && + member.definitionId !== null && + member.definitionStatus === "active" && + member.killSwitch === false, + ); + if (active.length !== 1) { + return { + handled: true, + queued: false, + duplicate: false, + reason: "agent_count", + }; + } + const agent = active[0]!; + if ( + !Array.isArray(agent.allowedRooms) || + !agent.allowedRooms.includes(input.roomId) || + !agent.definitionId || + !agent.runtime || + !agent.model || + !agent.promptVersion || + agent.maximumRuntimeSeconds === null || + agent.maximumTokenBudget === null || + agent.maximumCostCents === null + ) { + return { + handled: true, + queued: false, + duplicate: false, + reason: "agent_unavailable", + }; + } + + const agentId = agent.definitionId; + const runtime = agent.runtime; + const model = agent.model; + const promptVersion = agent.promptVersion; + const maximumRuntimeSeconds = agent.maximumRuntimeSeconds; + const maximumTokenBudget = agent.maximumTokenBudget; + const maximumCostCents = agent.maximumCostCents; + requireCapability(subject, "agents.invoke"); + const idempotencyKey = `agent-direct-message:${input.messageId}`; + const inputHash = createHash("sha256") + .update( + JSON.stringify({ + messageId: input.messageId, + roomId: input.roomId, + plainText: source.plainText, + }), + ) + .digest("hex"); + const deadlineAt = new Date(Date.now() + maximumRuntimeSeconds * 1_000); + + return this.db.transaction(async (tx) => { + const eligible = await tx + .select({ + id: schema.agentDefinitions.id, + allowedRooms: schema.agentDefinitions.allowedRooms, + }) + .from(schema.agentDefinitions) + .innerJoin( + schema.actors, + and( + eq(schema.actors.organisationId, subject.organisationId), + eq(schema.actors.id, schema.agentDefinitions.id), + eq(schema.actors.status, "active"), + ), + ) + .innerJoin( + schema.roomMemberships, + and( + eq(schema.roomMemberships.organisationId, subject.organisationId), + eq(schema.roomMemberships.roomId, input.roomId), + eq(schema.roomMemberships.actorId, schema.agentDefinitions.id), + or( + isNull(schema.roomMemberships.accessExpiresAt), + gt(schema.roomMemberships.accessExpiresAt, new Date()), + ), + ), + ) + .where( + and( + eq(schema.agentDefinitions.organisationId, subject.organisationId), + eq(schema.agentDefinitions.status, "active"), + eq(schema.agentDefinitions.killSwitch, false), + ), + ); + if ( + eligible.length !== 1 || + eligible[0]?.id !== agentId || + !Array.isArray(eligible[0].allowedRooms) || + !eligible[0].allowedRooms.includes(input.roomId) + ) { + throw new Error("Direct-message agent is unavailable"); + } + + const [inserted] = await tx + .insert(schema.agentRuns) + .values({ + id: newId(), + agentId, + organisationId: subject.organisationId, + roomId: input.roomId, + investigationId: source.relatedInvestigationId, + requestedByActorId: subject.actorId, + trigger: "direct_message", + status: "queued", + request: { + kind: "direct_message", + sourceMessageId: input.messageId, + humanRequest: source.plainText, + traceId, + }, + progress: { stage: "queued", percent: 0 }, + deadlineAt, + inputHash, + promptVersion, + runtime, + model, + maximumRuntimeSeconds, + maximumTokenBudget, + maximumCostCents, + idempotencyKey, + }) + .onConflictDoNothing() + .returning(); + const run = + inserted ?? + ( + await tx + .select() + .from(schema.agentRuns) + .where( + and( + eq(schema.agentRuns.organisationId, subject.organisationId), + eq(schema.agentRuns.idempotencyKey, idempotencyKey), + ), + ) + .limit(1) + )[0]; + if ( + !run || + run.agentId !== agentId || + run.roomId !== input.roomId || + run.requestedByActorId !== subject.actorId || + run.inputHash !== inputHash + ) { + throw new Error("Direct-message run idempotency conflict"); + } + if (inserted) { + await tx.insert(schema.agentRunEvents).values({ + id: newId(), + organisationId: subject.organisationId, + runId: run.id, + eventType: "queued", + message: "Direct-message agent run queued", + payload: { + trigger: "direct_message", + sourceMessageId: input.messageId, + roomId: input.roomId, + }, + }); + await appendAuditEvent(tx, { + organisationId: subject.organisationId, + actorId: subject.actorId, + actorType: "human", + action: "agent.run.queued", + targetType: "agent_run", + targetId: run.id, + metadata: { + trigger: "direct_message", + sourceMessageId: input.messageId, + roomId: input.roomId, + }, + traceId: redactObservationText(traceId), + }); + await writeOutbox(tx, { + organisationId: subject.organisationId, + eventType: "agent.run.queued", + aggregateType: "agent_run", + aggregateId: run.id, + queueName: "muster-agents", + payload: { runId: run.id }, + idempotencyKey: `agent.run.queued:${run.id}`, + traceId: redactObservationText(traceId), + }); + } + return { + handled: true, + queued: true, + duplicate: !inserted, + agentId: run.agentId, + agentRunId: run.id, + status: run.status, + }; + }); + } +} diff --git a/apps/web/lib/agent-gateway.ts b/apps/web/lib/agent-gateway.ts new file mode 100644 index 0000000..32b3dd1 --- /dev/null +++ b/apps/web/lib/agent-gateway.ts @@ -0,0 +1,8 @@ +export function agentGatewayHeaders(organisationId: string) { + const token = process.env.MUSTER_AGENT_GATEWAY_TOKEN?.trim(); + if (!token) throw new Error("Agent gateway token is not configured"); + return { + authorization: `Bearer ${token}`, + "x-muster-organisation-id": organisationId, + }; +} diff --git a/apps/web/lib/agent-learning-domain.integration.test.ts b/apps/web/lib/agent-learning-domain.integration.test.ts index dd3d422..e7bcbaa 100644 --- a/apps/web/lib/agent-learning-domain.integration.test.ts +++ b/apps/web/lib/agent-learning-domain.integration.test.ts @@ -181,21 +181,87 @@ describeIntegration("governed agent learning", () => { .where(eq(schema.agentRuns.id, sourceRunId)); if (!source) throw new Error("Source run missing"); const queuedRunId = newId(); + const directRunId = newId(); + const directRoomId = newId(); + const directMessageId = newId(); await database() - .insert(schema.agentRuns) + .insert(schema.rooms) + .values({ + id: directRoomId, + organisationId, + name: `synthetic-learning-direct-${directRoomId}`, + slug: `synthetic-learning-direct-${directRoomId}`, + displayName: "Synthetic learning direct room", + roomType: "direct", + visibility: "private", + createdByActorId: actorId, + }); + await database() + .insert(schema.roomMemberships) + .values([ + { + organisationId, + roomId: directRoomId, + actorId, + membershipRole: "owner", + }, + { + organisationId, + roomId: directRoomId, + actorId: agentId, + membershipRole: "agent_member", + }, + ]); + await database() + .insert(schema.messages) .values({ - ...source, - id: queuedRunId, - status: "queued", - startedAt: null, - completedAt: null, - heartbeatAt: null, - leaseExpiresAt: null, - cancellationRequestedAt: null, - cancellationReason: null, - progress: { stage: "queued", percent: 0 }, - idempotencyKey: `learning-kill-switch:${queuedRunId}`, + id: directMessageId, + organisationId, + roomId: directRoomId, + authorActorId: actorId, + messageType: "text", + document: { type: "doc", content: [] }, + plainText: "Synthetic kill-switch direct request", + idempotencyKey: `learning-kill-switch-message:${directMessageId}`, }); + await database() + .insert(schema.agentRuns) + .values([ + { + ...source, + id: queuedRunId, + status: "queued", + startedAt: null, + completedAt: null, + heartbeatAt: null, + leaseExpiresAt: null, + cancellationRequestedAt: null, + cancellationReason: null, + progress: { stage: "queued", percent: 0 }, + idempotencyKey: `learning-kill-switch:${queuedRunId}`, + }, + { + ...source, + id: directRunId, + roomId: directRoomId, + trigger: "direct_message", + status: "queued", + request: { + kind: "direct_message", + sourceMessageId: directMessageId, + humanRequest: "Synthetic kill-switch direct request", + traceId: `learning-kill-switch-direct:${directRunId}`, + }, + startedAt: null, + completedAt: null, + heartbeatAt: null, + leaseExpiresAt: null, + cancellationRequestedAt: null, + cancellationReason: null, + progress: { stage: "queued", percent: 0 }, + idempotencyKey: `learning-kill-switch:${directRunId}`, + }, + ]); await mutateAgentLearning(context(), { action: "set_kill_switch", enabled: true, @@ -215,6 +281,40 @@ describeIntegration("governed agent learning", () => { status: "cancelled", reason: expect.stringContaining("kill switch"), }); + const [reply, outbox] = await Promise.all([ + database() + .select() + .from(schema.messages) + .where( + eq( + schema.messages.idempotencyKey, + `agent-direct-message-reply:${directRunId}`, + ), + ), + database() + .select() + .from(schema.outboxEvents) + .where( + eq( + schema.outboxEvents.idempotencyKey, + `room.message.created:agent-direct-message:${directRunId}`, + ), + ), + ]); + expect(reply).toHaveLength(1); + expect(reply[0]).toMatchObject({ + roomId: directRoomId, + threadParentId: directMessageId, + relatedAgentRunId: directRunId, + messageType: "agent-status", + }); + expect(reply[0]?.document).toMatchObject({ + status: "cancelled", + failureCode: "agent_kill_switch", + sourceMessageId: directMessageId, + agentRunId: directRunId, + }); + expect(outbox).toHaveLength(1); await mutateAgentLearning(context(), { action: "set_kill_switch", enabled: false, diff --git a/apps/web/lib/agent-learning-domain.ts b/apps/web/lib/agent-learning-domain.ts index e40b24a..fa87a90 100644 --- a/apps/web/lib/agent-learning-domain.ts +++ b/apps/web/lib/agent-learning-domain.ts @@ -13,7 +13,7 @@ import { schema, writeOutbox, } from "@muster/database"; -import { and, desc, eq, max, or } from "drizzle-orm"; +import { and, desc, eq, gt, isNull, max, ne, or } from "drizzle-orm"; import { z } from "zod"; const LearningMutationSchema = z.discriminatedUnion("action", [ @@ -63,8 +63,22 @@ function strings(value: unknown): string[] { export async function agentLearningState( organisationId: string, agentId: string, + options: { includeInactive?: boolean } = {}, ) { const db = database(); + const memoryConditions = [ + eq(schema.agentMemories.organisationId, organisationId), + eq(schema.agentMemories.agentId, agentId), + ]; + if (!options.includeInactive) { + const now = new Date(); + memoryConditions.push(ne(schema.agentMemories.status, "rejected")); + const activeMemoryCondition = or( + isNull(schema.agentMemories.expiresAt), + gt(schema.agentMemories.expiresAt, now), + ); + if (activeMemoryCondition) memoryConditions.push(activeMemoryCondition); + } const [definition] = await db .select() .from(schema.agentDefinitions) @@ -81,12 +95,7 @@ export async function agentLearningState( db .select() .from(schema.agentMemories) - .where( - and( - eq(schema.agentMemories.organisationId, organisationId), - eq(schema.agentMemories.agentId, agentId), - ), - ) + .where(and(...memoryConditions)) .orderBy(desc(schema.agentMemories.createdAt)) .limit(100), db @@ -789,7 +798,14 @@ async function setKillSwitch( ), ), ) - .returning({ id: schema.agentRuns.id }) + .returning({ + id: schema.agentRuns.id, + organisationId: schema.agentRuns.organisationId, + agentId: schema.agentRuns.agentId, + roomId: schema.agentRuns.roomId, + investigationId: schema.agentRuns.investigationId, + request: schema.agentRuns.request, + }) : []; if (cancelledRuns.length > 0) { const safeReason = redactObservationText(reason); @@ -803,6 +819,92 @@ async function setKillSwitch( payload: { reason: safeReason }, })), ); + for (const run of cancelledRuns) { + const request = z + .object({ + kind: z.literal("direct_message"), + sourceMessageId: z.string().uuid(), + traceId: z.string().optional(), + }) + .safeParse(run.request); + if (!request.success || !run.roomId) continue; + const [source] = await tx + .select({ id: schema.messages.id }) + .from(schema.messages) + .innerJoin( + schema.rooms, + and( + eq(schema.rooms.organisationId, run.organisationId), + eq(schema.rooms.id, run.roomId), + eq(schema.rooms.roomType, "direct"), + isNull(schema.rooms.archivedAt), + ), + ) + .innerJoin( + schema.roomMemberships, + and( + eq(schema.roomMemberships.organisationId, run.organisationId), + eq(schema.roomMemberships.roomId, run.roomId), + eq(schema.roomMemberships.actorId, run.agentId), + or( + isNull(schema.roomMemberships.accessExpiresAt), + gt(schema.roomMemberships.accessExpiresAt, now), + ), + ), + ) + .where( + and( + eq(schema.messages.organisationId, run.organisationId), + eq(schema.messages.id, request.data.sourceMessageId), + eq(schema.messages.roomId, run.roomId), + isNull(schema.messages.deletedAt), + ), + ) + .limit(1); + if (!source) continue; + const [message] = await tx + .insert(schema.messages) + .values({ + id: newId(), + organisationId: run.organisationId, + roomId: run.roomId, + threadParentId: request.data.sourceMessageId, + authorActorId: run.agentId, + messageType: "agent-status", + document: { + type: "agent-direct-message-reply", + status: "cancelled", + sourceMessageId: request.data.sourceMessageId, + agentRunId: run.id, + failureCode: "agent_kill_switch", + }, + plainText: "The agent request was cancelled (agent_kill_switch).", + dataClassification: "internal", + relatedInvestigationId: run.investigationId, + relatedAgentRunId: run.id, + idempotencyKey: `agent-direct-message-reply:${run.id}`, + }) + .onConflictDoNothing() + .returning({ id: schema.messages.id }); + if (!message) continue; + await writeOutbox(tx, { + organisationId: run.organisationId, + eventType: "room.message.created", + aggregateType: "message", + aggregateId: message.id, + queueName: "muster-outbox", + payload: { + messageId: message.id, + roomId: run.roomId, + threadParentId: request.data.sourceMessageId, + agentRunId: run.id, + }, + idempotencyKey: `room.message.created:agent-direct-message:${run.id}`, + traceId: redactObservationText( + request.data.traceId ?? `agent-run-${run.id}`, + ), + }); + } } await appendAuditEvent(tx, { organisationId: context.organisationId, diff --git a/apps/web/lib/alfie-research-domain.ts b/apps/web/lib/alfie-research-domain.ts index 12f396e..2cab3a8 100644 --- a/apps/web/lib/alfie-research-domain.ts +++ b/apps/web/lib/alfie-research-domain.ts @@ -7,7 +7,7 @@ import { schema, writeOutbox, } from "@muster/database"; -import { and, eq } from "drizzle-orm"; +import { and, eq, isNull } from "drizzle-orm"; import { z } from "zod"; import { ApiProblem } from "./api-context"; @@ -90,7 +90,10 @@ export class AlfieResearchDomainService { .select() .from(schema.researchWatchlists) .where( - eq(schema.researchWatchlists.organisationId, subject.organisationId), + and( + eq(schema.researchWatchlists.organisationId, subject.organisationId), + isNull(schema.researchWatchlists.archivedAt), + ), ); } diff --git a/apps/web/lib/archive-visibility.test.ts b/apps/web/lib/archive-visibility.test.ts new file mode 100644 index 0000000..56c8d65 --- /dev/null +++ b/apps/web/lib/archive-visibility.test.ts @@ -0,0 +1,43 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const repositoryRoot = fileURLToPath(new URL("../../../", import.meta.url)); + +function repositoryFile(path: string) { + return readFileSync(new URL(path, `file://${repositoryRoot}/`), "utf8"); +} + +describe("archived synthetic artifact visibility", () => { + it("excludes archived parent records from normal collection queries", () => { + const collectionQueries = { + "apps/web/app/api/v1/tasks/route.ts": "schema.tasks.archivedAt", + "apps/web/app/api/v1/hunts/route.ts": "schema.huntRuns.archivedAt", + "apps/web/lib/connector-domain.ts": + "schema.integrationRecords.archivedAt", + "apps/web/lib/alfie-research-domain.ts": + "schema.researchWatchlists.archivedAt", + "apps/web/app/api/v1/reports/route.ts": + "schema.reportManifests.archivedAt", + "apps/web/app/api/v1/reports/schedules/route.ts": + "schema.reportSchedules.archivedAt", + }; + + for (const [path, column] of Object.entries(collectionQueries)) { + expect(repositoryFile(path)).toContain(`isNull(${column})`); + } + }); + + it("keeps inactive learning history behind an explicit managed option", () => { + const domain = repositoryFile("apps/web/lib/agent-learning-domain.ts"); + const route = repositoryFile( + "apps/web/app/api/v1/agents/[id]/learning/route.ts", + ); + + expect(domain).toContain("includeInactive?: boolean"); + expect(domain).toContain('ne(schema.agentMemories.status, "rejected")'); + expect(domain).toContain("gt(schema.agentMemories.expiresAt, now)"); + expect(route).toContain("includeInactive"); + expect(route).toContain('requireCapability(subject, "agents.manage")'); + }); +}); diff --git a/apps/web/lib/connector-domain.ts b/apps/web/lib/connector-domain.ts index 6e3d424..7ac6ecb 100644 --- a/apps/web/lib/connector-domain.ts +++ b/apps/web/lib/connector-domain.ts @@ -1,4 +1,4 @@ -import { and, count, desc, eq, gte, sql } from "drizzle-orm"; +import { and, count, desc, eq, gte, isNull, sql } from "drizzle-orm"; import { requireCapability, type AuthorisationSubject } from "@muster/authz"; import { appendAuditEvent, @@ -43,7 +43,10 @@ export class ConnectorDomainService { .select() .from(schema.integrationRecords) .where( - eq(schema.integrationRecords.organisationId, subject.organisationId), + and( + eq(schema.integrationRecords.organisationId, subject.organisationId), + isNull(schema.integrationRecords.archivedAt), + ), ) .orderBy(desc(schema.integrationRecords.updatedAt)); return records.map((record) => ({ diff --git a/apps/web/lib/demo-data.ts b/apps/web/lib/demo-data.ts index 85203fa..fc8b408 100644 --- a/apps/web/lib/demo-data.ts +++ b/apps/web/lib/demo-data.ts @@ -1,23 +1,85 @@ +const starterIds = { + organisation: "018f55d8-c4c7-7c3e-88ef-000000000001", + actors: { + jordan: "018f55d8-c4c7-7c3e-88ef-000000000010", + triage: "018f55d8-c4c7-7c3e-88ef-000000000020", + tawnyHunt: "018f55d8-c4c7-7c3e-88ef-000000000021", + threatIntel: "018f55d8-c4c7-7c3e-88ef-000000000025", + }, + rooms: { + soc: "018f55d8-c4c7-7c3e-88ef-000000000100", + activeIncidents: "018f55d8-c4c7-7c3e-88ef-000000000101", + threatIntel: "018f55d8-c4c7-7c3e-88ef-000000000102", + detection: "018f55d8-c4c7-7c3e-88ef-000000000103", + endpoint: "018f55d8-c4c7-7c3e-88ef-000000000104", + bower: "018f55d8-c4c7-7c3e-88ef-000000000105", + incident: "018f55d8-c4c7-7c3e-88ef-000000000106", + investigation: "018f55d8-c4c7-7c3e-88ef-000000000107", + alerts: "018f55d8-c4c7-7c3e-88ef-000000000108", + mayaDirect: "018f55d8-c4c7-7c3e-88ef-000000000109", + triageDirect: "018f55d8-c4c7-7c3e-88ef-000000000110", + tawnyDirect: "018f55d8-c4c7-7c3e-88ef-000000000111", + parkerDirect: "018f55d8-c4c7-7c3e-88ef-000000000112", + }, + investigation: "018f55d8-c4c7-7c3e-88ef-000000000200", +} as const; + +const demoIds = { + organisation: "019e7a10-0000-7000-8000-000000000001", + actors: { + jordan: "019e7a10-0000-7000-8000-000000000010", + maya: "019e7a10-0000-7000-8000-000000000011", + daniel: "019e7a10-0000-7000-8000-000000000012", + priya: "019e7a10-0000-7000-8000-000000000013", + alex: "019e7a10-0000-7000-8000-000000000014", + triage: "019e7a10-0000-7000-8000-000000000020", + tawnyHunt: "019e7a10-0000-7000-8000-000000000021", + bowerHealth: "019e7a10-0000-7000-8000-000000000022", + kelpieCase: "019e7a10-0000-7000-8000-000000000023", + sentinelQuery: "019e7a10-0000-7000-8000-000000000024", + threatIntel: "019e7a10-0000-7000-8000-000000000025", + }, + rooms: { + soc: "019e7a10-0000-7000-8000-000000000100", + activeIncidents: "019e7a10-0000-7000-8000-000000000101", + threatIntel: "019e7a10-0000-7000-8000-000000000102", + detection: "019e7a10-0000-7000-8000-000000000103", + endpoint: "019e7a10-0000-7000-8000-000000000104", + bower: "019e7a10-0000-7000-8000-000000000105", + incident: "019e7a10-0000-7000-8000-000000000106", + investigation: "019e7a10-0000-7000-8000-000000000107", + alerts: "019e7a10-0000-7000-8000-000000000108", + mayaDirect: "019e7a10-0000-7000-8000-000000000109", + triageDirect: "019e7a10-0000-7000-8000-000000000110", + tawnyDirect: "019e7a10-0000-7000-8000-000000000111", + parkerDirect: "019e7a10-0000-7000-8000-000000000112", + }, + investigation: "019e7a10-0000-7000-8000-000000000200", + messages: { + mayaParent: "019e7a10-0000-7000-8000-000000000701", + priyaParent: "019e7a10-0000-7000-8000-000000000705", + }, +} as const; + export type Severity = "critical" | "high" | "medium" | "low" | "informational"; -export const demoMode = - process.env.NEXT_PUBLIC_MUSTER_DEMO_MODE === "true"; +export const demoMode = process.env.NEXT_PUBLIC_MUSTER_DEMO_MODE === "true"; export const demoOrganisation = demoMode ? { - id: "018f55d8-c4c7-7c3e-88ef-000000000001", + id: demoIds.organisation, name: "Muster Demo Workspace", slug: "muster-demo", } : { - id: "018f55d8-c4c7-7c3e-88ef-000000000001", + id: starterIds.organisation, name: "Muster Workspace", slug: "muster", }; const demoPeopleRows = [ { - id: "018f55d8-c4c7-7c3e-88ef-000000000010", + id: demoIds.actors.jordan, name: "Jordan Blake", initials: "JB", role: "Security Lead", @@ -25,7 +87,7 @@ const demoPeopleRows = [ type: "human", }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000011", + id: demoIds.actors.maya, name: "Maya Chen", initials: "MC", role: "Senior Analyst", @@ -33,7 +95,7 @@ const demoPeopleRows = [ type: "human", }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000012", + id: demoIds.actors.daniel, name: "Daniel Brooks", initials: "DB", role: "Detection Engineer", @@ -41,7 +103,7 @@ const demoPeopleRows = [ type: "human", }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000013", + id: demoIds.actors.priya, name: "Priya Nair", initials: "PN", role: "Incident Responder", @@ -49,7 +111,7 @@ const demoPeopleRows = [ type: "human", }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000014", + id: demoIds.actors.alex, name: "Alex Morgan", initials: "AM", role: "Read-only Auditor", @@ -62,7 +124,7 @@ export const demoPeople = demoMode ? demoPeopleRows : ([ { - id: "018f55d8-c4c7-7c3e-88ef-000000000010", + id: starterIds.actors.jordan, name: "Muster Administrator", initials: "MA", role: "Administrator", @@ -73,7 +135,7 @@ export const demoPeople = demoMode const starterAgents = [ { - id: "018f55d8-c4c7-7c3e-88ef-000000000020", + id: demoIds.actors.triage, name: "Triage Agent", initials: "TA", purpose: "Correlates alerts and recommends disposition.", @@ -88,7 +150,7 @@ const starterAgents = [ killSwitch: false, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000021", + id: demoIds.actors.tawnyHunt, name: "Tawny Hunt Agent", initials: "TH", purpose: "Runs bounded endpoint telemetry hunts.", @@ -103,7 +165,7 @@ const starterAgents = [ killSwitch: false, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000022", + id: demoIds.actors.bowerHealth, name: "Bower Health Agent", initials: "BH", purpose: "Explains collector gaps and delivery posture.", @@ -118,7 +180,7 @@ const starterAgents = [ killSwitch: false, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000023", + id: demoIds.actors.kelpieCase, name: "Kelpie Case Agent", initials: "KC", purpose: "Drafts and synchronises formal case context.", @@ -133,7 +195,7 @@ const starterAgents = [ killSwitch: false, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000024", + id: demoIds.actors.sentinelQuery, name: "Sentinel Query Agent", initials: "SQ", purpose: "Builds and runs bounded KQL queries.", @@ -148,7 +210,7 @@ const starterAgents = [ killSwitch: false, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000025", + id: demoIds.actors.threatIntel, name: "Threat Intelligence Agent", initials: "TI", purpose: "Enriches indicators using approved sources.", @@ -166,9 +228,9 @@ const starterAgents = [ export const demoAgents = demoMode ? starterAgents - : [ + : ([ { - id: "018f55d8-c4c7-7c3e-88ef-000000000020", + id: starterIds.actors.triage, name: "Alfie", initials: "AL", purpose: @@ -184,7 +246,7 @@ export const demoAgents = demoMode killSwitch: false, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000021", + id: starterIds.actors.tawnyHunt, name: "Jessie", initials: "JE", purpose: @@ -204,7 +266,7 @@ export const demoAgents = demoMode killSwitch: false, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000025", + id: starterIds.actors.threatIntel, name: "Parker", initials: "PA", purpose: @@ -219,18 +281,90 @@ export const demoAgents = demoMode successRate: "—", killSwitch: false, }, - ] as const; + ] as const); const demoRoomRows = [ - { slug: "soc-operations", name: "soc-operations", topic: "Daily coordination, shift handover and operational updates", unread: 8, mentions: 2, type: "operations", favourite: true }, - { slug: "alerts", name: "alerts", topic: "Incoming security signals and triage discussion", unread: 12, mentions: 3, type: "system", favourite: true }, - { slug: "active-incidents", name: "active-incidents", topic: "Coordination for active security incidents", unread: 4, mentions: 1, type: "incident", favourite: true }, - { slug: "threat-intelligence", name: "threat-intelligence", topic: "Indicator enrichment and intelligence sharing", unread: 0, mentions: 0, type: "operations", favourite: false }, - { slug: "detection-engineering", name: "detection-engineering", topic: "Detection proposals, reviews and releases", unread: 3, mentions: 0, type: "engineering", favourite: false }, - { slug: "endpoint-security", name: "endpoint-security", topic: "Tawny detections, endpoint hunts and response", unread: 0, mentions: 0, type: "operations", favourite: false }, - { slug: "bower-telemetry-health", name: "bower-telemetry-health", topic: "Collector posture, source coverage and delivery health", unread: 1, mentions: 0, type: "system", favourite: false }, - { slug: "incident-KP-2026-0042", name: "incident-KP-2026-0042", topic: "Malicious PowerShell — credential access", unread: 6, mentions: 2, type: "incident", favourite: true }, - { slug: "investigation-suspicious-powershell", name: "investigation-suspicious-powershell", topic: "Correlating Bower identity signals with Tawny endpoint activity", unread: 5, mentions: 1, type: "investigation", favourite: true }, + { + slug: "soc-operations", + name: "soc-operations", + topic: "Daily coordination, shift handover and operational updates", + unread: 8, + mentions: 2, + type: "operations", + favourite: true, + }, + { + slug: "alerts", + name: "alerts", + topic: "Incoming security signals and triage discussion", + unread: 12, + mentions: 3, + type: "system", + favourite: true, + }, + { + slug: "active-incidents", + name: "active-incidents", + topic: "Coordination for active security incidents", + unread: 4, + mentions: 1, + type: "incident", + favourite: true, + }, + { + slug: "threat-intelligence", + name: "threat-intelligence", + topic: "Indicator enrichment and intelligence sharing", + unread: 0, + mentions: 0, + type: "operations", + favourite: false, + }, + { + slug: "detection-engineering", + name: "detection-engineering", + topic: "Detection proposals, reviews and releases", + unread: 3, + mentions: 0, + type: "engineering", + favourite: false, + }, + { + slug: "endpoint-security", + name: "endpoint-security", + topic: "Tawny detections, endpoint hunts and response", + unread: 0, + mentions: 0, + type: "operations", + favourite: false, + }, + { + slug: "bower-telemetry-health", + name: "bower-telemetry-health", + topic: "Collector posture, source coverage and delivery health", + unread: 1, + mentions: 0, + type: "system", + favourite: false, + }, + { + slug: "incident-KP-2026-0042", + name: "incident-KP-2026-0042", + topic: "Malicious PowerShell — credential access", + unread: 6, + mentions: 2, + type: "incident", + favourite: true, + }, + { + slug: "investigation-suspicious-powershell", + name: "investigation-suspicious-powershell", + topic: "Correlating Bower identity signals with Tawny endpoint activity", + unread: 5, + mentions: 1, + type: "investigation", + favourite: true, + }, ] as const; export const demoRooms = demoMode @@ -272,6 +406,14 @@ const demoDirectRoomRows = [ presence: "away", agent: true, }, + { + slug: "dm-parker", + name: "Parker", + topic: "Operational reports and executive briefings", + initials: "PA", + presence: "online", + agent: true, + }, ] as const; export const demoDirectRooms = demoMode @@ -303,22 +445,24 @@ export const demoDirectRooms = demoMode }, ] as const); +const activeIds = demoMode ? demoIds : starterIds; + export const roomIdBySlug: Record = { - "soc-operations": "018f55d8-c4c7-7c3e-88ef-000000000100", - "active-incidents": "018f55d8-c4c7-7c3e-88ef-000000000101", - "threat-intelligence": "018f55d8-c4c7-7c3e-88ef-000000000102", - "detection-engineering": "018f55d8-c4c7-7c3e-88ef-000000000103", - "endpoint-security": "018f55d8-c4c7-7c3e-88ef-000000000104", - "bower-telemetry-health": "018f55d8-c4c7-7c3e-88ef-000000000105", - "incident-KP-2026-0042": "018f55d8-c4c7-7c3e-88ef-000000000106", - "investigation-suspicious-powershell": "018f55d8-c4c7-7c3e-88ef-000000000107", - alerts: "018f55d8-c4c7-7c3e-88ef-000000000108", - "dm-maya-chen": "018f55d8-c4c7-7c3e-88ef-000000000109", - "dm-triage-agent": "018f55d8-c4c7-7c3e-88ef-000000000110", - "dm-tawny-hunt-agent": "018f55d8-c4c7-7c3e-88ef-000000000111", - "dm-alfie": "018f55d8-c4c7-7c3e-88ef-000000000110", - "dm-jessie": "018f55d8-c4c7-7c3e-88ef-000000000111", - "dm-parker": "018f55d8-c4c7-7c3e-88ef-000000000112", + "soc-operations": activeIds.rooms.soc, + "active-incidents": activeIds.rooms.activeIncidents, + "threat-intelligence": activeIds.rooms.threatIntel, + "detection-engineering": activeIds.rooms.detection, + "endpoint-security": activeIds.rooms.endpoint, + "bower-telemetry-health": activeIds.rooms.bower, + "incident-KP-2026-0042": activeIds.rooms.incident, + "investigation-suspicious-powershell": activeIds.rooms.investigation, + alerts: activeIds.rooms.alerts, + "dm-maya-chen": activeIds.rooms.mayaDirect, + "dm-triage-agent": activeIds.rooms.triageDirect, + "dm-tawny-hunt-agent": activeIds.rooms.tawnyDirect, + "dm-alfie": activeIds.rooms.triageDirect, + "dm-jessie": activeIds.rooms.tawnyDirect, + "dm-parker": activeIds.rooms.parkerDirect, }; const demoAlertRows = [ @@ -392,7 +536,7 @@ const demoAlertRows = [ export const demoAlerts = demoMode ? demoAlertRows : []; export const activeInvestigation = { - id: "018f55d8-c4c7-7c3e-88ef-000000000200", + id: activeIds.investigation, number: "INV-2026-0178", title: "Legacy portal credential access and suspicious PowerShell", severity: "critical" as Severity, @@ -410,7 +554,8 @@ export const activeInvestigation = { hypotheses: [ { id: "HYP-12", - statement: "Stolen portal credentials were used before endpoint execution.", + statement: + "Stolen portal credentials were used before endpoint execution.", status: "supported", confidence: 84, support: 4, @@ -499,7 +644,7 @@ const demoRoomTimeline = [ body: "INV-2026-0178 created from ALT-2026-1041 and ALT-2026-1042.", }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000701", + id: demoIds.messages.mayaParent, type: "human", author: "Maya Chen", initials: "MC", @@ -543,7 +688,7 @@ const demoRoomTimeline = [ meta: "FND-87 · 5 evidence references · human reviewed", }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000705", + id: demoIds.messages.priyaParent, type: "human", author: "Priya Nair", initials: "PN", @@ -791,9 +936,23 @@ export const integrationData = { ], rows: [ ["legacy-portal-au-01", "Active", "12 s ago", "0", "Healthy", "3 / 3"], - ["legacy-finance-au-02", "Active", "34 min ago", "284", "Degraded", "4 / 5"], + [ + "legacy-finance-au-02", + "Active", + "34 min ago", + "284", + "Degraded", + "4 / 5", + ], ["customer-api-au-01", "Active", "18 s ago", "0", "Healthy", "6 / 6"], - ["warehouse-erp-au-01", "Pending", "Never", "0", "Awaiting approval", "0 / 4"], + [ + "warehouse-erp-au-01", + "Pending", + "Never", + "0", + "Awaiting approval", + "0 / 4", + ], ], }, tawny: { @@ -810,10 +969,31 @@ export const integrationData = { ["Actions pending", "1"], ], rows: [ - ["WS-1042", "Online", "12 s ago", "Windows 11", "High", "Isolation pending"], + [ + "WS-1042", + "Online", + "12 s ago", + "Windows 11", + "High", + "Isolation pending", + ], ["WS-1098", "Online", "8 s ago", "Windows 11", "Medium", "No action"], - ["SRV-FIN-02", "Online", "21 s ago", "Windows Server 2022", "Low", "No action"], - ["LAP-2041", "Offline", "2 h ago", "macOS 15", "Informational", "No action"], + [ + "SRV-FIN-02", + "Online", + "21 s ago", + "Windows Server 2022", + "Low", + "No action", + ], + [ + "LAP-2041", + "Offline", + "2 h ago", + "macOS 15", + "Informational", + "No action", + ], ], }, kelpie: { @@ -830,10 +1010,38 @@ export const integrationData = { ["Last sync", "42 s"], ], rows: [ - ["KP-2026-0042", "Credential access and endpoint execution", "Containment", "Critical", "Priya Nair", "2 min ago"], - ["KP-2026-0039", "Exposed cloud service principal", "Investigation", "High", "Jordan Blake", "14 min ago"], - ["KP-2026-0037", "Suspicious mailbox forwarding rule", "Monitoring", "Medium", "Maya Chen", "1 h ago"], - ["KP-2026-0033", "Public storage container", "Resolved", "Low", "Daniel Brooks", "Yesterday"], + [ + "KP-2026-0042", + "Credential access and endpoint execution", + "Containment", + "Critical", + "Priya Nair", + "2 min ago", + ], + [ + "KP-2026-0039", + "Exposed cloud service principal", + "Investigation", + "High", + "Jordan Blake", + "14 min ago", + ], + [ + "KP-2026-0037", + "Suspicious mailbox forwarding rule", + "Monitoring", + "Medium", + "Maya Chen", + "1 h ago", + ], + [ + "KP-2026-0033", + "Public storage container", + "Resolved", + "Low", + "Daniel Brooks", + "Yesterday", + ], ], }, } as const; @@ -843,19 +1051,22 @@ const demoSearchResults = [ group: "Messages", title: "Encoded PowerShell retrieved second-stage content", context: "#investigation-suspicious-powershell · Tawny Hunt Agent", - snippet: "Found a PowerShell process tree, two outbound connections, and one file write…", + snippet: + "Found a PowerShell process tree, two outbound connections, and one file write…", }, { group: "Alerts", title: "ALT-2026-1042 · Suspicious PowerShell with encoded command", context: "Tawny · critical · WS-1042", - snippet: "Sigma rule sigma-123 matched powershell.exe with encoded command line.", + snippet: + "Sigma rule sigma-123 matched powershell.exe with encoded command line.", }, { group: "Investigations", title: "INV-2026-0178 · Legacy portal credential access", context: "Awaiting approval · Maya Chen", - snippet: "Bower authentication failures and Tawny endpoint activity correlate on jsmith…", + snippet: + "Bower authentication failures and Tawny endpoint activity correlate on jsmith…", }, { group: "Cases", @@ -867,7 +1078,8 @@ const demoSearchResults = [ group: "Findings", title: "FND-87 · Encoded PowerShell retrieved content", context: "94% confidence · 5 evidence references", - snippet: "Contacted cdn-auth-check.example and wrote update.dat before execution.", + snippet: + "Contacted cdn-auth-check.example and wrote update.dat before execution.", }, { group: "Evidence", diff --git a/apps/web/lib/object-storage.ts b/apps/web/lib/object-storage.ts index 2495071..f33f138 100644 --- a/apps/web/lib/object-storage.ts +++ b/apps/web/lib/object-storage.ts @@ -1,151 +1,8 @@ -import { createHash, createHmac } from "node:crypto"; - -export type EvidenceObject = { - storageKey: string; - contentType: string; - body: Uint8Array; -}; - -export interface EvidenceObjectStorage { - putObject(object: EvidenceObject): Promise; -} - -export interface ContentObjectStorage extends EvidenceObjectStorage { - getObject(storageKey: string): Promise; -} - -function sha256(value: string | Uint8Array) { - return createHash("sha256").update(value).digest("hex"); -} - -function hmac(key: string | Buffer, value: string) { - return createHmac("sha256", key).update(value).digest(); -} - -function objectUrl(endpoint: string, bucket: string, storageKey: string) { - const url = new URL(endpoint); - const basePath = url.pathname.replace(/\/$/, ""); - const encodedKey = storageKey - .split("/") - .map((part) => encodeURIComponent(part)) - .join("/"); - url.pathname = `${basePath}/${encodeURIComponent(bucket)}/${encodedKey}`; - return url; -} - -function signingHeaders( - method: "GET" | "PUT", - url: URL, - body: Uint8Array, - region: string, - accessKey: string, - secretKey: string, - contentType?: string, -) { - const now = new Date(); - const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, ""); - const date = amzDate.slice(0, 8); - const payloadHash = sha256(body); - const canonicalHeaders = [ - ...(contentType ? [`content-type:${contentType}`] : []), - `host:${url.host}`, - `x-amz-content-sha256:${payloadHash}`, - `x-amz-date:${amzDate}`, - ].join("\n"); - const signedHeaders = contentType - ? "content-type;host;x-amz-content-sha256;x-amz-date" - : "host;x-amz-content-sha256;x-amz-date"; - const canonicalRequest = [ - method, - url.pathname, - "", - canonicalHeaders, - "", - signedHeaders, - payloadHash, - ].join("\n"); - const scope = `${date}/${region}/s3/aws4_request`; - const stringToSign = [ - "AWS4-HMAC-SHA256", - amzDate, - scope, - sha256(canonicalRequest), - ].join("\n"); - const dateKey = hmac(`AWS4${secretKey}`, date); - const regionKey = hmac(dateKey, region); - const serviceKey = hmac(regionKey, "s3"); - const signingKey = hmac(serviceKey, "aws4_request"); - const signature = createHmac("sha256", signingKey) - .update(stringToSign) - .digest("hex"); - return { - ...(contentType ? { "content-type": contentType } : {}), - "x-amz-content-sha256": payloadHash, - "x-amz-date": amzDate, - authorization: - `AWS4-HMAC-SHA256 Credential=${accessKey}/${scope},` + - ` SignedHeaders=${signedHeaders}, Signature=${signature}`, - }; -} - -function storageConfiguration() { - return { - endpoint: process.env.OBJECT_STORAGE_ENDPOINT ?? "http://127.0.0.1:9000", - bucket: process.env.OBJECT_STORAGE_BUCKET ?? "muster-evidence", - region: process.env.OBJECT_STORAGE_REGION ?? "us-east-1", - accessKey: process.env.OBJECT_STORAGE_ACCESS_KEY ?? "muster", - secretKey: process.env.OBJECT_STORAGE_SECRET_KEY ?? "local-minio-secret", - }; -} - -export const defaultObjectStorage: ContentObjectStorage = { - async putObject(object) { - const { endpoint, bucket, region, accessKey, secretKey } = - storageConfiguration(); - const url = objectUrl(endpoint, bucket, object.storageKey); - const response = await fetch(url, { - method: "PUT", - headers: signingHeaders( - "PUT", - url, - object.body, - region, - accessKey, - secretKey, - object.contentType, - ), - body: Buffer.from(object.body), - }); - if (!response.ok) { - throw new Error( - `Object storage rejected upload with status ${response.status}`, - ); - } - }, - async getObject(storageKey) { - const { endpoint, bucket, region, accessKey, secretKey } = - storageConfiguration(); - const url = objectUrl(endpoint, bucket, storageKey); - const emptyBody = new Uint8Array(); - const response = await fetch(url, { - method: "GET", - headers: signingHeaders( - "GET", - url, - emptyBody, - region, - accessKey, - secretKey, - ), - }); - if (!response.ok) { - throw new Error( - `Object storage rejected download with status ${response.status}`, - ); - } - return new Uint8Array(await response.arrayBuffer()); - }, -}; - -export const defaultEvidenceObjectStorage: EvidenceObjectStorage = - defaultObjectStorage; +export { + defaultEvidenceObjectStorage, + defaultObjectStorage, + type CleanupObjectStorage, + type ContentObjectStorage, + type EvidenceObject, + type EvidenceObjectStorage, +} from "@muster/evidence"; diff --git a/apps/web/lib/synthetic-cleanup-domain.test.ts b/apps/web/lib/synthetic-cleanup-domain.test.ts new file mode 100644 index 0000000..95ea589 --- /dev/null +++ b/apps/web/lib/synthetic-cleanup-domain.test.ts @@ -0,0 +1,186 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import * as database from "@muster/database"; +import { + syntheticCleanupManifestDigest, + syntheticCleanupTableDigest, + syntheticCleanupTableKeys, + type SyntheticCleanupManifest, + type SyntheticCleanupPlan, +} from "@muster/database"; +import { SyntheticCleanupDomainService } from "./synthetic-cleanup-domain.ts"; + +const evidenceId = "019fa400-0000-7000-8000-000000000001"; +const plan: SyntheticCleanupPlan = { + version: 2, + manifestId: "019fa400-0000-7000-8000-000000000002", + approvalId: "019fa400-0000-7000-8000-000000000003", + organisationId: "019fa400-0000-7000-8000-000000000004", + maintenanceActorId: "019fa400-0000-7000-8000-000000000005", + generatedAt: "2026-07-27T00:00:00.000Z", + archiveRoomIds: [], + archiveTaskIds: [], + archiveHuntIds: [], + archiveIntegrationIds: [], + archiveResearchWatchlistIds: [], + archiveReportManifestIds: [], + archiveReportScheduleIds: [], + hideMessageIds: [], + retireEvidenceIds: [evidenceId], + rejectAgentMemoryIds: [], + retireActorIds: [], + selectionEvidence: [ + { + table: "evidence", + recordId: evidenceId, + provenanceId: "019fa400-0000-7000-8000-000000000006", + }, + ], + objectStorageObjects: [ + { + evidenceId, + bucket: "muster-evidence", + key: `synthetic/${evidenceId}`, + versionId: "synthetic-version-1", + etag: "synthetic-etag", + size: 42, + sha256: "a".repeat(64), + legalHold: false, + objectLockMetadata: {}, + }, + ], +}; + +function manifest(): SyntheticCleanupManifest { + const tableDigests = Object.fromEntries( + syntheticCleanupTableKeys.map((table) => [ + table, + syntheticCleanupTableDigest([]), + ]), + ) as SyntheticCleanupManifest["tableDigests"]; + const unsigned = { ...plan, tableDigests }; + return { + ...unsigned, + digest: syntheticCleanupManifestDigest(unsigned), + }; +} + +const subject = { + actorId: plan.maintenanceActorId, + organisationId: plan.organisationId, + capabilities: new Set(["administration.manage"] as const), +}; + +describe("synthetic cleanup maintenance endpoint", () => { + afterEach(() => vi.restoreAllMocks()); + + it("rejects an unauthorised subject before capture work", async () => { + const capture = vi.spyOn(database, "captureSyntheticCleanupManifest"); + await expect( + new SyntheticCleanupDomainService().execute( + { ...subject, capabilities: new Set() }, + { mode: "capture", payload: plan }, + "trace-forbidden", + ), + ).rejects.toThrow("Missing capability"); + expect(capture).not.toHaveBeenCalled(); + }); + + it("rejects a tampered manifest before database or external work", async () => { + const verify = vi.spyOn(database, "verifySyntheticCleanup"); + await expect( + new SyntheticCleanupDomainService().execute( + subject, + { + mode: "verify", + payload: { ...manifest(), generatedAt: "2026-07-27T01:00:00.000Z" }, + }, + "trace-tampered", + ), + ).rejects.toThrow("digest mismatch"); + expect(verify).not.toHaveBeenCalled(); + }); + + it("queues object deletion instead of doing storage work in HTTP", async () => { + const captured = manifest(); + vi.spyOn(database, "findSyntheticCleanupReceipt").mockResolvedValueOnce( + null, + ); + vi.spyOn(database, "applySyntheticCleanup").mockResolvedValueOnce({ + applied: true, + manifestId: captured.manifestId, + objectStorageObjects: captured.objectStorageObjects, + } as never); + await expect( + new SyntheticCleanupDomainService().execute( + subject, + { mode: "apply", payload: captured }, + "trace-apply", + ), + ).resolves.toMatchObject({ + applied: true, + objectDeletionQueued: true, + pendingObjectVersions: 1, + }); + }); + + it("reports receipt outcomes without requeueing deletion", async () => { + const captured = manifest(); + vi.spyOn(database, "findSyntheticCleanupReceipt").mockResolvedValueOnce({ + manifestId: captured.manifestId, + } as never); + vi.spyOn(database, "applySyntheticCleanup").mockResolvedValueOnce({ + applied: false, + manifestId: captured.manifestId, + receipt: { manifestId: captured.manifestId }, + } as never); + vi.spyOn( + database, + "listSyntheticCleanupObjectDeletionAttempts", + ).mockResolvedValueOnce([ + { + evidenceId, + versionId: "synthetic-version-1", + result: "succeeded", + }, + ] as never); + await expect( + new SyntheticCleanupDomainService().execute( + subject, + { mode: "apply", payload: captured }, + "trace-replay", + ), + ).resolves.toMatchObject({ + applied: false, + objectDeletionQueued: false, + deletedOrReconciledObjectVersions: 1, + pendingObjectVersions: 0, + }); + }); + + it("queues only a freshly authorised retry", async () => { + const captured = manifest(); + vi.spyOn( + database, + "authoriseSyntheticCleanupObjectRetry", + ).mockResolvedValueOnce({ + authorised: true, + pendingObjects: captured.objectStorageObjects, + }); + await expect( + new SyntheticCleanupDomainService().execute( + subject, + { + mode: "retry_object_deletion", + payload: { + manifest: captured, + retryApprovalId: "019fa400-0000-7000-8000-000000000099", + }, + }, + "trace-retry", + ), + ).resolves.toMatchObject({ + authorised: true, + objectDeletionQueued: true, + }); + }); +}); diff --git a/apps/web/lib/synthetic-cleanup-domain.ts b/apps/web/lib/synthetic-cleanup-domain.ts new file mode 100644 index 0000000..9f5eba2 --- /dev/null +++ b/apps/web/lib/synthetic-cleanup-domain.ts @@ -0,0 +1,121 @@ +import { + applySyntheticCleanup, + authoriseSyntheticCleanupObjectRetry, + captureSyntheticCleanupManifest, + findSyntheticCleanupReceipt, + listSyntheticCleanupObjectDeletionAttempts, + parseSyntheticCleanupManifest, + requestSyntheticCleanupApproval, + requestSyntheticCleanupObjectRetryApproval, + SyntheticCleanupObjectRetrySchema, + SyntheticCleanupPlanSchema, + verifySyntheticCleanup, +} from "@muster/database"; +import { + ForbiddenError, + requireCapability, + type AuthorisationSubject, +} from "@muster/authz"; +import { z } from "zod"; + +const RequestSchema = z + .object({ + mode: z.enum([ + "capture", + "verify", + "request_approval", + "apply", + "request_object_deletion_retry", + "retry_object_deletion", + ]), + payload: z.unknown(), + }) + .strict(); + +function validateSubject( + subject: AuthorisationSubject, + payload: { organisationId: string; maintenanceActorId: string }, +) { + requireCapability(subject, "administration.manage"); + if ( + subject.organisationId !== payload.organisationId || + subject.actorId !== payload.maintenanceActorId + ) { + throw new ForbiddenError("administration.manage"); + } +} + +export class SyntheticCleanupDomainService { + async execute(subject: AuthorisationSubject, raw: unknown, traceId: string) { + const input = RequestSchema.parse(raw); + if ( + input.mode === "request_object_deletion_retry" || + input.mode === "retry_object_deletion" + ) { + const retry = SyntheticCleanupObjectRetrySchema.parse(input.payload); + const manifest = parseSyntheticCleanupManifest(retry.manifest); + validateSubject(subject, manifest); + if (input.mode === "request_object_deletion_retry") { + return requestSyntheticCleanupObjectRetryApproval( + subject, + { ...retry, manifest }, + traceId, + ); + } + const authorised = await authoriseSyntheticCleanupObjectRetry( + subject, + { ...retry, manifest }, + traceId, + ); + return { + ...authorised, + objectDeletionQueued: authorised.pendingObjects.length > 0, + }; + } + + if (input.mode === "capture") { + const plan = SyntheticCleanupPlanSchema.parse(input.payload); + validateSubject(subject, plan); + return captureSyntheticCleanupManifest(subject, plan); + } + + const manifest = parseSyntheticCleanupManifest(input.payload); + validateSubject(subject, manifest); + if (input.mode === "verify") { + return verifySyntheticCleanup(subject, manifest); + } + if (input.mode === "request_approval") { + return requestSyntheticCleanupApproval(subject, manifest, traceId); + } + + const priorReceipt = await findSyntheticCleanupReceipt(subject, manifest); + const result = await applySyntheticCleanup(subject, manifest, traceId); + if (!result.applied || priorReceipt) { + const attempts = await listSyntheticCleanupObjectDeletionAttempts( + subject, + manifest, + ); + const completed = new Set( + attempts + .filter( + (attempt) => + attempt.result === "succeeded" || + attempt.result === "observed_missing", + ) + .map((attempt) => `${attempt.evidenceId}:${attempt.versionId}`), + ); + return { + ...result, + deletedOrReconciledObjectVersions: completed.size, + pendingObjectVersions: + manifest.objectStorageObjects.length - completed.size, + objectDeletionQueued: false, + }; + } + return { + ...result, + pendingObjectVersions: manifest.objectStorageObjects.length, + objectDeletionQueued: manifest.objectStorageObjects.length > 0, + }; + } +} diff --git a/apps/worker/package.json b/apps/worker/package.json index 0b16762..38b40aa 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -16,6 +16,7 @@ "@muster/config": "workspace:*", "@muster/contracts": "workspace:*", "@muster/database": "workspace:*", + "@muster/evidence": "workspace:*", "@muster/integrations": "workspace:*", "bullmq": "5.81.2", "drizzle-orm": "0.45.2", diff --git a/apps/worker/src/index.ts b/apps/worker/src/index.ts index 217d3b9..1656010 100644 --- a/apps/worker/src/index.ts +++ b/apps/worker/src/index.ts @@ -20,6 +20,7 @@ import { schema, writeOutbox, } from "@muster/database"; +import { processSyntheticCleanupObjectDeletion } from "./synthetic-cleanup-object.ts"; import { ConnectorConfigurationSchema, GovernedConnectorError, @@ -47,6 +48,7 @@ import { researchRunIdempotencyKey, staleResearchEvidence, } from "./research-scheduler.ts"; +import { appendResearchTerminalMessage } from "./research-status.ts"; import { queueDueParkerReports } from "./parker-scheduler.ts"; import { processParkerReport } from "./parker-report.ts"; @@ -110,6 +112,17 @@ const authoritativeProcessor: Processor = async (job) => { job.data.aggregateId, ); } + if ( + job.queueName === "muster-maintenance" && + job.name === "maintenance.synthetic_cleanup.object_delete.queued" + ) { + await processSyntheticCleanupObjectDeletion({ + organisationId: job.data.organisationId, + aggregateType: job.data.aggregateType, + aggregateId: job.data.aggregateId, + traceId: job.data.traceId, + }); + } if ( job.queueName === "muster-maintenance" && job.name === "research.run.queued" @@ -146,9 +159,12 @@ const authoritativeProcessor: Processor = async (job) => { job.queueName === "muster-agents" && job.name !== "report.generate.queued" ) { + const gatewayToken = process.env.MUSTER_AGENT_GATEWAY_TOKEN?.trim(); + if (!gatewayToken) throw new Error("Agent gateway token is not configured"); const response = await fetch( `${process.env.AGENT_GATEWAY_URL ?? "http://agent-gateway:3002"}/v1/runs/dispatch`, { + headers: { authorization: `Bearer ${gatewayToken}` }, method: "POST", signal: AbortSignal.timeout(10_000), }, @@ -1648,7 +1664,7 @@ async function processResearchRun( ) .limit(1); if (!run) throw new Error("Authoritative research run not found"); - if (run.run.status === "completed") return; + if (run.run.status === "completed" || run.run.status === "failed") return; if (!strings(run.agent.allowedTools).includes("research.feeds.read")) throw new Error("Alfie feed tool is revoked"); await db.transaction(async (tx) => { @@ -1911,6 +1927,14 @@ async function processResearchRun( eq(schema.agentRuns.id, run.run.agentRunId), ), ); + if (posted === 0) { + await appendResearchTerminalMessage(tx, { + organisationId, + researchRunId, + kind: "no_changes", + traceId, + }); + } await appendAuditEvent(tx, { organisationId, actorId: run.agent.id, @@ -1973,6 +1997,12 @@ async function processResearchRun( eq(schema.agentRuns.id, run.run.agentRunId), ), ); + await appendResearchTerminalMessage(tx, { + organisationId, + researchRunId, + kind: "failed", + traceId, + }); await appendAuditEvent(tx, { organisationId, actorId: run.agent.id, diff --git a/apps/worker/src/research-config.test.ts b/apps/worker/src/research-config.test.ts new file mode 100644 index 0000000..eb31f12 --- /dev/null +++ b/apps/worker/src/research-config.test.ts @@ -0,0 +1,33 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const repositoryRoot = fileURLToPath(new URL("../../../", import.meta.url)); + +function repositoryFile(path: string) { + return readFileSync(new URL(path, `file://${repositoryRoot}/`), "utf8"); +} + +describe("Alfie homelab research configuration", () => { + it("passes an explicitly configured feed-origin allowlist to web and worker only", () => { + const compose = repositoryFile("deploy/docker/docker-compose.homelab.yml"); + expect( + compose.match( + /MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS: \$\{MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS:-\}/g, + ), + ).toHaveLength(2); + }); + + it("documents an empty safe default without enabling a mock origin", () => { + for (const path of [".env.example", "deploy/docker/.env.homelab.example"]) { + const example = repositoryFile(path); + expect(example).toMatch(/^MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS=$/m); + expect(example).not.toMatch( + /^MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS=.*https?:/m, + ); + } + expect(repositoryFile("docs/operations/alfie-research.md")).toContain( + "Homelab Compose passes this value unchanged to web and worker services", + ); + }); +}); diff --git a/apps/worker/src/research-status.test.ts b/apps/worker/src/research-status.test.ts new file mode 100644 index 0000000..8f7a3a1 --- /dev/null +++ b/apps/worker/src/research-status.test.ts @@ -0,0 +1,307 @@ +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { closeDatabase, database, newId, schema } from "@muster/database"; +import { and, eq, inArray } from "drizzle-orm"; +import { + appendResearchTerminalMessage, + researchTerminalMessageText, + type ResearchTerminalMessageKind, +} from "./research-status.ts"; + +const describeIntegration = + process.env.MUSTER_INTEGRATION_TESTS === "true" + ? describe.sequential + : describe.skip; + +describe("research terminal status copy", () => { + it("uses fixed redacted copy for terminal failure and no-change success", () => { + expect(researchTerminalMessageText("failed")).toBe( + "Alfie research could not complete after the final retry. No feed content was posted. Review the agent run before retrying.", + ); + expect(researchTerminalMessageText("no_changes")).toBe( + "Alfie research completed. No new or changed findings were found.", + ); + }); +}); + +describeIntegration("research terminal status persistence", () => { + const db = database(); + const watchlistIds: string[] = []; + const researchRunIds: string[] = []; + const agentRunIds: string[] = []; + let organisationId = ""; + let agentId = ""; + let roomId = ""; + let promptVersion = ""; + let runtime = ""; + let model = ""; + + beforeAll(async () => { + const [agent] = await db + .select() + .from(schema.agentDefinitions) + .where( + and( + eq(schema.agentDefinitions.name, "Alfie"), + eq(schema.agentDefinitions.status, "active"), + eq(schema.agentDefinitions.killSwitch, false), + ), + ) + .limit(1); + if ( + !agent || + !Array.isArray(agent.allowedRooms) || + typeof agent.allowedRooms[0] !== "string" + ) { + throw new Error("Bootstrap Alfie with an allowed room before tests"); + } + organisationId = agent.organisationId; + agentId = agent.id; + roomId = agent.allowedRooms[0]; + promptVersion = agent.systemPromptVersion; + runtime = agent.runtime; + model = agent.model; + }); + + async function terminalRun( + kind: ResearchTerminalMessageKind, + canary: string, + posted = 0, + ) { + const watchlistId = newId(); + const researchRunId = newId(); + const agentRunId = newId(); + watchlistIds.push(watchlistId); + researchRunIds.push(researchRunId); + agentRunIds.push(agentRunId); + const terminalStatus = kind === "failed" ? "failed" : "completed"; + const sourceUrl = `https://secret.example/feed?token=${canary}`; + + await db.insert(schema.researchWatchlists).values({ + id: watchlistId, + organisationId, + roomId, + createdByActorId: agentId, + name: `Synthetic terminal visibility ${watchlistId}`, + vendors: [], + technologies: [], + sources: [{ name: canary, url: sourceUrl }], + cadenceMinutes: 240, + enabled: false, + nextRunAt: new Date(Date.now() + 86_400_000), + }); + await db.insert(schema.agentRuns).values({ + id: agentRunId, + agentId, + organisationId, + roomId, + requestedByActorId: agentId, + trigger: "schedule", + status: terminalStatus, + request: { researchRunId, watchlistId, secret: canary }, + progress: { stage: terminalStatus, percent: 100 }, + inputHash: "0".repeat(64), + promptVersion, + runtime, + model, + structuredOutput: + kind === "no_changes" ? { posted, sources: 1, briefHashes: [] } : null, + error: kind === "failed" ? `Bearer ${canary} from ${sourceUrl}` : null, + failureCode: kind === "failed" ? "research_feed_failed" : null, + completedAt: new Date(), + idempotencyKey: `test:research-terminal-agent:${agentRunId}`, + }); + await db.insert(schema.researchRuns).values({ + id: researchRunId, + organisationId, + watchlistId, + agentRunId, + status: terminalStatus, + sourceLimit: 5, + tokenBudget: 1_000, + costLimitCents: 10, + timeLimitSeconds: 60, + idempotencyKey: `test:research-terminal:${researchRunId}`, + completedAt: new Date(), + error: kind === "failed" ? `Bearer ${canary} from ${sourceUrl}` : null, + }); + return { researchRunId, agentRunId, sourceUrl }; + } + + async function append( + researchRunId: string, + kind: ResearchTerminalMessageKind, + scopedOrganisationId = organisationId, + ) { + return db.transaction((tx) => + appendResearchTerminalMessage(tx, { + organisationId: scopedOrganisationId, + researchRunId, + kind, + traceId: `test:research-terminal:${researchRunId}`, + }), + ); + } + + it("appends one scoped redacted failure message and remains replay safe", async () => { + const canary = `synthetic-research-secret-${newId()}`; + const run = await terminalRun("failed", canary); + + expect(await append(run.researchRunId, "failed", newId())).toBeNull(); + const firstMessageId = await append(run.researchRunId, "failed"); + expect(firstMessageId).toMatch( + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, + ); + expect(await append(run.researchRunId, "failed")).toBeNull(); + + const messages = await db + .select() + .from(schema.messages) + .where( + and( + eq(schema.messages.organisationId, organisationId), + eq( + schema.messages.idempotencyKey, + `research.status.message:${run.researchRunId}`, + ), + ), + ); + const outbox = await db + .select() + .from(schema.outboxEvents) + .where( + eq( + schema.outboxEvents.idempotencyKey, + `room.message.created:research-status:${run.researchRunId}`, + ), + ); + + expect(messages).toHaveLength(1); + expect(messages[0]).toMatchObject({ + id: firstMessageId, + organisationId, + roomId, + authorActorId: agentId, + messageType: "agent-status", + relatedAgentRunId: run.agentRunId, + }); + expect(messages[0]?.document).toMatchObject({ + type: "research-run-status", + researchRunId: run.researchRunId, + agentRunId: run.agentRunId, + status: "failed", + failureCode: "research_feed_failed", + trust: "agent-status", + }); + expect(outbox).toHaveLength(1); + expect(outbox[0]).toMatchObject({ + organisationId, + eventType: "room.message.created", + aggregateType: "message", + aggregateId: firstMessageId, + payload: { messageId: firstMessageId, roomId }, + }); + const visibleRecord = JSON.stringify({ messages, outbox }); + expect(visibleRecord).not.toContain(canary); + expect(visibleRecord).not.toContain(run.sourceUrl); + expect(visibleRecord).not.toContain("secret.example"); + }); + + it("appends one no-change success message and refuses a run with findings", async () => { + const noChanges = await terminalRun( + "no_changes", + `synthetic-no-change-${newId()}`, + ); + const withFinding = await terminalRun( + "no_changes", + `synthetic-finding-${newId()}`, + 1, + ); + + const firstMessageId = await append(noChanges.researchRunId, "no_changes"); + expect(firstMessageId).toBeTruthy(); + expect(await append(noChanges.researchRunId, "no_changes")).toBeNull(); + expect(await append(withFinding.researchRunId, "no_changes")).toBeNull(); + + const messages = await db + .select() + .from(schema.messages) + .where( + eq( + schema.messages.idempotencyKey, + `research.status.message:${noChanges.researchRunId}`, + ), + ); + const withFindingMessages = await db + .select() + .from(schema.messages) + .where( + eq( + schema.messages.idempotencyKey, + `research.status.message:${withFinding.researchRunId}`, + ), + ); + expect(messages).toHaveLength(1); + expect(messages[0]).toMatchObject({ + plainText: + "Alfie research completed. No new or changed findings were found.", + relatedAgentRunId: noChanges.agentRunId, + }); + expect(messages[0]?.document).toMatchObject({ + status: "completed_no_changes", + researchRunId: noChanges.researchRunId, + }); + expect(withFindingMessages).toHaveLength(0); + }); + + afterAll(async () => { + for (const researchRunId of researchRunIds) { + await db + .delete(schema.outboxEvents) + .where( + eq( + schema.outboxEvents.idempotencyKey, + `room.message.created:research-status:${researchRunId}`, + ), + ); + await db + .delete(schema.messages) + .where( + eq( + schema.messages.idempotencyKey, + `research.status.message:${researchRunId}`, + ), + ); + } + if (researchRunIds.length > 0) { + await db + .delete(schema.researchRuns) + .where( + and( + eq(schema.researchRuns.organisationId, organisationId), + inArray(schema.researchRuns.id, researchRunIds), + ), + ); + } + for (const agentRunId of agentRunIds) { + await db + .delete(schema.agentRuns) + .where( + and( + eq(schema.agentRuns.organisationId, organisationId), + eq(schema.agentRuns.id, agentRunId), + ), + ); + } + for (const watchlistId of watchlistIds) { + await db + .delete(schema.researchWatchlists) + .where( + and( + eq(schema.researchWatchlists.organisationId, organisationId), + eq(schema.researchWatchlists.id, watchlistId), + ), + ); + } + await closeDatabase(); + }); +}); diff --git a/apps/worker/src/research-status.ts b/apps/worker/src/research-status.ts new file mode 100644 index 0000000..d35f06c --- /dev/null +++ b/apps/worker/src/research-status.ts @@ -0,0 +1,108 @@ +import { database, newId, schema, writeOutbox } from "@muster/database"; +import { and, eq } from "drizzle-orm"; + +type Transaction = Parameters< + Parameters["transaction"]>[0] +>[0]; + +export type ResearchTerminalMessageKind = "failed" | "no_changes"; + +export function researchTerminalMessageText(kind: ResearchTerminalMessageKind) { + return kind === "failed" + ? "Alfie research could not complete after the final retry. No feed content was posted. Review the agent run before retrying." + : "Alfie research completed. No new or changed findings were found."; +} + +export async function appendResearchTerminalMessage( + tx: Transaction, + input: { + organisationId: string; + researchRunId: string; + kind: ResearchTerminalMessageKind; + traceId: string; + }, +) { + const expectedStatus = input.kind === "failed" ? "failed" : "completed"; + const [run] = await tx + .select({ + agentRunId: schema.researchRuns.agentRunId, + agentId: schema.agentRuns.agentId, + roomId: schema.researchWatchlists.roomId, + structuredOutput: schema.agentRuns.structuredOutput, + }) + .from(schema.researchRuns) + .innerJoin( + schema.researchWatchlists, + and( + eq(schema.researchWatchlists.organisationId, input.organisationId), + eq(schema.researchWatchlists.id, schema.researchRuns.watchlistId), + ), + ) + .innerJoin( + schema.agentRuns, + and( + eq(schema.agentRuns.organisationId, input.organisationId), + eq(schema.agentRuns.id, schema.researchRuns.agentRunId), + eq(schema.agentRuns.status, expectedStatus), + ), + ) + .where( + and( + eq(schema.researchRuns.organisationId, input.organisationId), + eq(schema.researchRuns.id, input.researchRunId), + eq(schema.researchRuns.status, expectedStatus), + ), + ) + .limit(1); + if (!run) return null; + + if (input.kind === "no_changes") { + const output = + run.structuredOutput !== null && + typeof run.structuredOutput === "object" && + !Array.isArray(run.structuredOutput) + ? (run.structuredOutput as Record) + : null; + if (output?.posted !== 0) return null; + } + + const messageId = newId(); + const [message] = await tx + .insert(schema.messages) + .values({ + id: messageId, + organisationId: input.organisationId, + roomId: run.roomId, + authorActorId: run.agentId, + messageType: "agent-status", + document: { + type: "research-run-status", + researchRunId: input.researchRunId, + agentRunId: run.agentRunId, + status: input.kind === "failed" ? "failed" : "completed_no_changes", + ...(input.kind === "failed" + ? { failureCode: "research_feed_failed" } + : {}), + trust: "agent-status", + }, + plainText: researchTerminalMessageText(input.kind), + dataClassification: "internal", + relatedAgentRunId: run.agentRunId, + idempotencyKey: `research.status.message:${input.researchRunId}`, + }) + .onConflictDoNothing() + .returning({ id: schema.messages.id }); + if (!message) return null; + + await writeOutbox(tx, { + organisationId: input.organisationId, + eventType: "room.message.created", + aggregateType: "message", + aggregateId: message.id, + queueName: "muster-outbox", + payload: { messageId: message.id, roomId: run.roomId }, + idempotencyKey: `room.message.created:research-status:${input.researchRunId}`, + traceId: input.traceId, + }); + return message.id; +} diff --git a/apps/worker/src/synthetic-cleanup-object.test.ts b/apps/worker/src/synthetic-cleanup-object.test.ts new file mode 100644 index 0000000..8c0eb65 --- /dev/null +++ b/apps/worker/src/synthetic-cleanup-object.test.ts @@ -0,0 +1,101 @@ +import { describe, expect, it } from "vitest"; +import { + assertCleanupObjectContent, + assertCleanupObjectVersion, + canReconcileMissingObject, +} from "./synthetic-cleanup-object.ts"; + +const object = { + evidenceId: "019fa500-0000-7000-8000-000000000001", + bucket: "muster-evidence", + key: "synthetic/evidence.bin", + versionId: "immutable-version-1", + etag: "synthetic-etag", + size: 42, + sha256: "a".repeat(64), + legalHold: false as const, + objectLockMetadata: {}, +}; + +describe("synthetic cleanup object worker", () => { + it("accepts only the exact unlocked immutable version", () => { + expect(() => + assertCleanupObjectVersion(object, { + versionId: object.versionId, + etag: object.etag, + size: object.size, + legalHold: false, + objectLockMetadata: {}, + }), + ).not.toThrow(); + }); + + it.each([ + null, + { + versionId: "replacement-version", + etag: object.etag, + size: object.size, + legalHold: false, + objectLockMetadata: {}, + }, + { + versionId: object.versionId, + etag: object.etag, + size: object.size, + legalHold: true, + objectLockMetadata: {}, + }, + ])("rejects missing, replaced, or held versions", (actual) => { + expect(() => assertCleanupObjectVersion(object, actual)).toThrow( + "metadata changed or is locked", + ); + }); + + it("reconciles missing only after a started attempt or fresh retry approval", () => { + expect( + canReconcileMissingObject( + "cleanup_manifest", + object, + "019fa500-0000-7000-8000-000000000002", + [], + ), + ).toBe(false); + expect( + canReconcileMissingObject( + "cleanup_manifest", + object, + "019fa500-0000-7000-8000-000000000002", + [ + { + evidenceId: object.evidenceId, + versionId: object.versionId, + authorizationApprovalId: "019fa500-0000-7000-8000-000000000002", + result: "started", + }, + ], + ), + ).toBe(true); + expect( + canReconcileMissingObject( + "cleanup_object_retry_approval", + object, + "019fa500-0000-7000-8000-000000000003", + [], + ), + ).toBe(true); + }); + + it("requires the exact version bytes before deletion", () => { + const body = new TextEncoder().encode("synthetic cleanup bytes"); + const expected = { + size: body.byteLength, + sha256: + "14891e06ce2b55d8366b303bda6a77dfdae4ea662102e96b08f767b61458eae2", + }; + expect(() => assertCleanupObjectContent(expected, body)).not.toThrow(); + expect(() => + assertCleanupObjectContent(expected, new TextEncoder().encode("changed")), + ).toThrow("content digest changed"); + }); +}); diff --git a/apps/worker/src/synthetic-cleanup-object.ts b/apps/worker/src/synthetic-cleanup-object.ts new file mode 100644 index 0000000..bb265b9 --- /dev/null +++ b/apps/worker/src/synthetic-cleanup-object.ts @@ -0,0 +1,310 @@ +import { createHash } from "node:crypto"; +import { and, eq } from "drizzle-orm"; +import { z } from "zod"; +import { + database, + parseSyntheticCleanupManifest, + recordSyntheticCleanupObjectDeletionAttempt, + schema, + type SyntheticCleanupObject, +} from "@muster/database"; +import { + defaultObjectStorage, + type CleanupObjectStorage, +} from "@muster/evidence"; + +export type SyntheticCleanupObjectJob = { + organisationId: string; + aggregateType: string; + aggregateId: string; + traceId: string; +}; + +export function assertCleanupObjectVersion( + expected: SyntheticCleanupObject, + actual: Awaited>, +) { + if ( + !actual || + actual.size !== expected.size || + actual.etag !== expected.etag || + actual.versionId !== expected.versionId || + actual.legalHold !== expected.legalHold || + JSON.stringify(actual.objectLockMetadata) !== + JSON.stringify(expected.objectLockMetadata) || + actual.legalHold || + Object.keys(actual.objectLockMetadata).length > 0 + ) { + throw new Error("Cleanup object version metadata changed or is locked"); + } +} + +export function canReconcileMissingObject( + aggregateType: string, + object: Pick, + authorizationApprovalId: string, + attempts: ReadonlyArray<{ + evidenceId: string; + versionId: string; + authorizationApprovalId: string; + result: string; + }>, +) { + return ( + aggregateType === "cleanup_object_retry_approval" || + attempts.some( + (attempt) => + attempt.evidenceId === object.evidenceId && + attempt.versionId === object.versionId && + attempt.authorizationApprovalId === authorizationApprovalId && + attempt.result === "started", + ) + ); +} + +export function assertCleanupObjectContent( + expected: Pick, + body: Uint8Array, +) { + const bodyDigest = createHash("sha256").update(body).digest("hex"); + if (body.byteLength !== expected.size || bodyDigest !== expected.sha256) { + throw new Error("Cleanup object version content digest changed"); + } +} + +export async function processSyntheticCleanupObjectDeletion( + input: SyntheticCleanupObjectJob, + storage: CleanupObjectStorage = defaultObjectStorage, +) { + const db = database(); + let manifestId = input.aggregateId; + let authorizationApprovalId: string | undefined; + if (input.aggregateType === "cleanup_object_retry_approval") { + const [approval] = await db + .select({ + id: schema.approvals.id, + actionType: schema.approvals.actionType, + status: schema.approvals.status, + target: schema.approvals.target, + }) + .from(schema.approvals) + .where( + and( + eq(schema.approvals.organisationId, input.organisationId), + eq(schema.approvals.id, input.aggregateId), + ), + ) + .limit(1); + const target = z + .object({ manifestId: z.uuid() }) + .safeParse(approval?.target); + if ( + !approval || + approval.status !== "executed" || + approval.actionType !== + "maintenance.synthetic-cleanup.object-delete-retry" || + !target.success + ) { + throw new Error("Executed cleanup object retry approval is unavailable"); + } + manifestId = target.data.manifestId; + authorizationApprovalId = approval.id; + } else if (input.aggregateType !== "cleanup_manifest") { + throw new Error("Unsupported cleanup object job target"); + } + + const [receipt] = await db + .select({ + approvalId: schema.syntheticCleanupReceipts.approvalId, + maintenanceActorId: schema.syntheticCleanupReceipts.maintenanceActorId, + manifest: schema.syntheticCleanupReceipts.manifest, + }) + .from(schema.syntheticCleanupReceipts) + .where( + and( + eq( + schema.syntheticCleanupReceipts.organisationId, + input.organisationId, + ), + eq(schema.syntheticCleanupReceipts.manifestId, manifestId), + ), + ) + .limit(1); + if (!receipt) throw new Error("Synthetic cleanup receipt is unavailable"); + const manifest = parseSyntheticCleanupManifest(receipt.manifest); + authorizationApprovalId ??= receipt.approvalId; + + const [actor] = await db + .select({ + status: schema.actors.status, + actorType: schema.actors.actorType, + capabilities: schema.actors.capabilityAssignments, + }) + .from(schema.actors) + .where( + and( + eq(schema.actors.organisationId, input.organisationId), + eq(schema.actors.id, receipt.maintenanceActorId), + ), + ) + .limit(1); + const capabilities = z.array(z.string()).safeParse(actor?.capabilities); + if ( + !actor || + actor.status !== "active" || + actor.actorType !== "human" || + !capabilities.success || + !capabilities.data.includes("administration.manage") + ) { + throw new Error("Cleanup maintenance actor is no longer authorised"); + } + const subject = { + actorId: receipt.maintenanceActorId, + organisationId: input.organisationId, + capabilities: new Set(["administration.manage"] as const), + }; + const attempts = await db + .select({ + evidenceId: schema.syntheticCleanupObjectDeletionAttempts.evidenceId, + versionId: schema.syntheticCleanupObjectDeletionAttempts.versionId, + authorizationApprovalId: + schema.syntheticCleanupObjectDeletionAttempts.authorizationApprovalId, + result: schema.syntheticCleanupObjectDeletionAttempts.result, + }) + .from(schema.syntheticCleanupObjectDeletionAttempts) + .where( + and( + eq( + schema.syntheticCleanupObjectDeletionAttempts.organisationId, + input.organisationId, + ), + eq( + schema.syntheticCleanupObjectDeletionAttempts.manifestId, + manifest.manifestId, + ), + ), + ); + const completed = new Set( + attempts + .filter( + (attempt) => + attempt.result === "succeeded" || + attempt.result === "observed_missing", + ) + .map((attempt) => `${attempt.evidenceId}:${attempt.versionId}`), + ); + const configuredBucket = + process.env.OBJECT_STORAGE_BUCKET ?? "muster-evidence"; + let completedCount = 0; + for (const object of manifest.objectStorageObjects) { + const objectKey = `${object.evidenceId}:${object.versionId}`; + if (completed.has(objectKey)) continue; + let preflightCode = "BucketMismatch"; + let before: Awaited>; + try { + if (object.bucket !== configuredBucket) { + throw new Error("Cleanup object bucket changed"); + } + preflightCode = "MetadataReadFailed"; + before = await storage.headObject(object.key, object.versionId); + } catch (error) { + await recordSyntheticCleanupObjectDeletionAttempt( + subject, + manifest, + object, + authorizationApprovalId, + "failed", + input.traceId, + preflightCode, + ); + throw error; + } + if (!before) { + if ( + !canReconcileMissingObject( + input.aggregateType, + object, + authorizationApprovalId, + attempts, + ) + ) { + await recordSyntheticCleanupObjectDeletionAttempt( + subject, + manifest, + object, + authorizationApprovalId, + "failed", + input.traceId, + "MissingBeforeDeletion", + ); + throw new Error( + "Cleanup object version was never verified before deletion", + ); + } + await recordSyntheticCleanupObjectDeletionAttempt( + subject, + manifest, + object, + authorizationApprovalId, + "observed_missing", + input.traceId, + ); + completedCount += 1; + continue; + } + try { + preflightCode = "MetadataMismatchOrLocked"; + assertCleanupObjectVersion(object, before); + preflightCode = "ContentReadFailed"; + const body = await storage.getObjectVersion(object.key, object.versionId); + preflightCode = "ContentDigestMismatch"; + assertCleanupObjectContent(object, body); + } catch (error) { + await recordSyntheticCleanupObjectDeletionAttempt( + subject, + manifest, + object, + authorizationApprovalId, + "failed", + input.traceId, + preflightCode, + ); + throw error; + } + await recordSyntheticCleanupObjectDeletionAttempt( + subject, + manifest, + object, + authorizationApprovalId, + "started", + input.traceId, + ); + try { + await storage.deleteObject(object.key, object.versionId); + if (await storage.headObject(object.key, object.versionId)) { + throw new Error("Cleanup object version remains after deletion"); + } + await recordSyntheticCleanupObjectDeletionAttempt( + subject, + manifest, + object, + authorizationApprovalId, + "succeeded", + input.traceId, + ); + completedCount += 1; + } catch (error) { + await recordSyntheticCleanupObjectDeletionAttempt( + subject, + manifest, + object, + authorizationApprovalId, + "failed", + input.traceId, + error instanceof Error ? error.name : "UnknownError", + ); + throw error; + } + } + return { completedObjectVersions: completedCount }; +} diff --git a/deploy/docker/.env.homelab.example b/deploy/docker/.env.homelab.example index 684bdb4..3a84c41 100644 --- a/deploy/docker/.env.homelab.example +++ b/deploy/docker/.env.homelab.example @@ -11,6 +11,9 @@ BETTER_AUTH_SECRET=generate-better-auth-secret OBJECT_STORAGE_ACCESS_KEY=muster OBJECT_STORAGE_SECRET_KEY=generate-object-storage-secret CONNECTOR_ENCRYPTION_KEY=generate-connector-encryption-key +MUSTER_AGENT_GATEWAY_TOKEN=generate-agent-gateway-token KELPIE_API_TOKEN=mock-kelpie-token TAWNY_API_TOKEN=mock-tawny-token BOWER_API_TOKEN=mock-bower-token +# Optional comma-separated HTTPS origins for additional Alfie research feeds. +MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS= diff --git a/deploy/docker/README.md b/deploy/docker/README.md index c80d260..8685751 100644 --- a/deploy/docker/README.md +++ b/deploy/docker/README.md @@ -11,10 +11,12 @@ MUSTER_IMAGE=ghcr.io/jusso-dev/muster@sha256:75ebdad962373ff1fa5dbef8dba8f0a005d ``` The installer creates a mode-600 `.env.homelab`, generates independent -database, authentication, storage, and administrator secrets, pulls the public -reviewed digest, starts the stack, waits for health, and creates the local -administrator. Replace the digest only after reviewing the corresponding -release evidence; mutable tags such as `latest` are rejected. +database, authentication, storage, internal agent-gateway, and administrator +secrets, pulls the public reviewed digest, starts the stack, waits for health, +and creates the local administrator. Existing installs missing +`MUSTER_AGENT_GATEWAY_TOKEN` receive one during upgrade. Replace the digest only +after reviewing the corresponding release evidence; mutable tags such as +`latest` are rejected. Only Muster's configured HTTP port is published. The example uses `http://muster.example.lan:3004`; replace it with the exact trusted browser origin. Use an HTTPS reverse proxy and set `AUTH_SECURE_COOKIES=true` when diff --git a/deploy/docker/docker-compose.homelab.yml b/deploy/docker/docker-compose.homelab.yml index 6310d20..40ce46a 100644 --- a/deploy/docker/docker-compose.homelab.yml +++ b/deploy/docker/docker-compose.homelab.yml @@ -30,6 +30,7 @@ x-muster-environment: &muster-environment MUSTER_MOCK_INTEGRATIONS: "true" MUSTER_AGENT_RUNTIME: ${MUSTER_AGENT_RUNTIME:-codex} AGENT_GATEWAY_URL: http://agent-gateway:3002 + MUSTER_AGENT_GATEWAY_TOKEN: ${MUSTER_AGENT_GATEWAY_TOKEN} CODEX_HOME: /var/lib/muster/codex x-muster-service: &muster-service @@ -108,6 +109,7 @@ services: environment: <<: *muster-environment MUSTER_HEALTHCHECK_URL: http://localhost:3000/api/v1/health + MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS: ${MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS:-} ports: - "${MUSTER_HTTP_PORT:-3004}:3000" depends_on: @@ -134,6 +136,7 @@ services: environment: <<: *muster-environment MUSTER_HEALTHCHECK_URL: http://localhost:3001/ready + MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS: ${MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS:-} command: ["/app/worker/dist/index.js"] depends_on: web: diff --git a/docker-compose.yml b/docker-compose.yml index 003ba02..e78ee73 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,7 @@ x-muster-environment: &muster-environment MUSTER_MOCK_INTEGRATIONS: "true" MUSTER_AGENT_RUNTIME: ${MUSTER_AGENT_RUNTIME:-codex} AGENT_GATEWAY_URL: http://agent-gateway:3002 + MUSTER_AGENT_GATEWAY_TOKEN: ${MUSTER_AGENT_GATEWAY_TOKEN:-local-agent-gateway-token-change-me-32-characters} CODEX_HOME: /var/lib/muster/codex services: diff --git a/docs/operations/alfie-research.md b/docs/operations/alfie-research.md index de0cbcb..8e86071 100644 --- a/docs/operations/alfie-research.md +++ b/docs/operations/alfie-research.md @@ -1,6 +1,6 @@ # Alfie governed research -Alfie reads only allowlisted HTTPS feeds. CISA KEV is built in. Additional origins require `MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS` as a comma-separated origin allowlist before an administrator creates a watchlist. +Alfie reads only allowlisted HTTPS feeds. CISA KEV is built in. Additional origins require `MUSTER_RESEARCH_ALLOWED_FEED_ORIGINS` as a comma-separated origin allowlist before an administrator creates a watchlist. Homelab Compose passes this value unchanged to web and worker services; leave it empty when no additional feed origin is approved. `POST /api/v1/research-watchlists` requires `agents.manage`. A watchlist has vendor/technology terms, a room, a 15-minute to 7-day cadence, and bounded source count. PostgreSQL stores watchlists, runs, source-backed briefs, feedback, and audit events; BullMQ only carries durable identifiers. diff --git a/docs/operations/backup-restore.md b/docs/operations/backup-restore.md index c6b62c0..9e948b4 100644 --- a/docs/operations/backup-restore.md +++ b/docs/operations/backup-restore.md @@ -1,5 +1,14 @@ # Backup and restore +For governed synthetic cleanup, use the stricter two-restore proof in +`docs/synthetic-cleanup.md`. A successful restore command alone is not cleanup +proof. Record source revision, backup SHA-256, per-organisation counts and +ordered row digests, protected direct-message/room/membership rows, evidence +object inventory, and audit-chain result before mutation. Restore the same +backup twice into separate isolated PostgreSQL instances: one to exercise the +exact approved manifest with workers and object credentials disabled, and one +to re-establish the untouched baseline. + Back up PostgreSQL with point-in-time recovery and the evidence bucket with versioning/object lock. Redis is rebuildable execution infrastructure; retain it only for operational continuity. Back up deployment configuration and secret references separately. Restore order: diff --git a/docs/operations/incident-recovery.md b/docs/operations/incident-recovery.md index 2ba480a..40d5f53 100644 --- a/docs/operations/incident-recovery.md +++ b/docs/operations/incident-recovery.md @@ -5,3 +5,40 @@ If Muster itself is suspected compromised: activate the agent kill switch; block Validate audit chains and compare connector delivery logs to authoritative product timelines. Rotate secrets before restoring service. Reprocess only undispatched or reconciled idempotency keys. Never replay response actions from raw queue data. Document containment, evidence custody, restoration point, tenant impact, and any audit-chain gap in the authoritative incident case. + +## Audit integrity verification and attestation + +Verify one organisation at a time after a restore or integrity incident: + +```bash +MUSTER_AUDIT_ORGANISATION_ID= pnpm db:verify-audit +``` + +The command emits an operator-visible JSON report and does not write, repair, +delete, or otherwise alter audit history. It exits `0` only for +`strict-valid`, `2` for `legacy-compatible-not-strict`, and `1` for `invalid`. + +`legacy-compatible-not-strict` identifies only the known historical defect: +an integration-action event was hashed before PostgreSQL JSONB omitted an +`approvalId: undefined` property. The report proves that specific legacy hash +is reproducible, but strict verification remains failed. It is not a repair, +does not make the historical chain strictly valid, and must never be presented +as one. + +For either non-strict result: + +1. Preserve the original report, the immutable database backup, and the + affected organisation ID with the incident evidence. +2. Record report time, application revision, verification command, strict + failure sequence, and any listed legacy sequence in an approval-backed + recovery attestation. +3. For `legacy-compatible-not-strict`, state that the result is a + compatibility reconstruction only; no audit event or outbox event was + rewritten or removed. +4. For `invalid`, treat the mismatch as unexplained: contain writes as needed, + export the affected rows/evidence, investigate source and backup lineage, + and obtain explicit incident approval before restoring service. +5. Attach the report and approval record to the authoritative incident case. + +Never update audit hashes, metadata, sequences, or outbox history merely to +make verification green. Audit history is append-only and immutable. diff --git a/docs/synthetic-cleanup.md b/docs/synthetic-cleanup.md new file mode 100644 index 0000000..42ea9bc --- /dev/null +++ b/docs/synthetic-cleanup.md @@ -0,0 +1,99 @@ +# Synthetic cleanup maintenance + +This runner archives or retires an exact, independently reviewed set of +synthetic records. It is not a selector and has no broad-delete mode. + +## Safety contract + +- One organisation per manifest. Every query and mutation is organisation + scoped. +- Every candidate must have an exact, pre-existing row in the append-only + `synthetic_artifact_provenance` registry. Human-readable names and + idempotency-key text are never cleanup proof. Record provenance when a seed, + mock run, or test fixture creates the artifact; legacy live-proof provenance + requires its own reviewed database change before cleanup capture. +- Authenticated `capture` locks the exact candidate and provenance rows, + records their live SHA-256 table digests, then returns the complete + digest-bound manifest. +- `verify` fails if any candidate is missing, cross-organisation, changed + after capture, already archived, or otherwise protected. +- The maintenance actor must be an active human with + `administration.manage`. +- The authenticated approval-request action creates an expiring + `maintenance.synthetic-cleanup` approval bound to the exact manifest digest. + Apply requires an independent approver, current approver capability, and the + unchanged live pre-state. +- Capture, verify, approval request, and apply require an authenticated web + session whose organisation, actor, and `administration.manage` capability + match the manifest. There is no plan-derived CLI identity. +- The four explicitly protected genuine messages can never be selected. No + direct room, direct-room message, or direct-room member can be archived, + redacted, or retired by this workflow. +- Legal-held, object-locked, already-retired, and stale evidence fails the + whole transaction. Unversioned and versioning-suspended `null` objects are + rejected because they cannot be deleted without a replacement-object race. + Nothing is silently skipped. +- Rooms and top-level product records are archived. Messages receive an + append-only deletion revision before redaction. Evidence metadata and hashes, + execution events/sources, audit, and outbox history remain intact. +- Exact affected-row counts are asserted. One serializable transaction writes + state changes, executed approval, immutable cleanup receipt, audit, and + outbox. +- Cleanup receipts are protected by a database trigger against update/delete. + Object-deletion attempts are append-only too. Replaying the same manifest + returns the receipt and recorded outcomes without deleting objects again. + +## Procedure + +1. Capture a PostgreSQL custom-format backup and SHA-256 digest. Inventory each + selected evidence object by bucket, key, version, ETag, size, SHA-256, legal + hold, and object-lock state. The inventory must cover selected evidence + one-to-one. +2. Restore the backup into isolated PostgreSQL with no worker, outbox consumer, + or object-store credentials. Apply the exact application revision's + migrations. +3. Build an unsigned version-2 plan with explicit UUID arrays, + `selectionEvidence`, and `objectStorageObjects`. Supply fresh `manifestId` + and `approvalId` values. +4. Through an authenticated administrator session, POST + `{"mode":"capture","payload":}` to + `/api/v1/maintenance/synthetic-cleanup`. Save and review the returned full + manifest and digest. Never derive an administrator identity from plan JSON. +5. POST `{"mode":"verify","payload":}` and then + `{"mode":"request_approval","payload":}` to + `/api/v1/maintenance/synthetic-cleanup`. Have a different authorised + administrator decide the request through the normal approvals workflow. + +6. In the isolated restore, apply the approved manifest. Assert exact receipt + counts/digests; protected, cross-organisation, bootstrap, and held rows + remain unchanged; the original audit chain only gains requested/applied + events. +7. Restore the original backup into a second isolated database and prove its + baseline counts/digests match the pre-cleanup inventory. Discard both + isolated environments. +8. Repeat capture and approval against live state only if unchanged. Apply by + posting `{"mode":"apply","payload":}` to the authenticated + maintenance endpoint. + +9. Apply commits an idempotent `muster-maintenance` outbox event. The worker, + never the HTTP handler, reloads the immutable receipt and executed approval, + rechecks each exact object version, deletes only receipt-listed immutable + versions, downloads the exact version and verifies its SHA-256, records + append-only started/succeeded/failed outcomes bound to that approval, and + verifies each version is absent after deletion. A missing version fails the + initial job unless the same approval already recorded `started`; this + distinguishes crash recovery from an object that was never verified. + Receipt replays never enqueue deletion. For a failed/crashed deletion, POST + `{"mode":"request_object_deletion_retry","payload":{"manifest":,"retryApprovalId":""}}`; + after a different active administrator approves that exact pending-version + digest, POST the same payload with mode `retry_object_deletion`. This + transaction consumes the approval and queues a new durable worker job. + Missing versions are then recorded as `observed_missing`; present versions + are rechecked and deleted by exact immutable version ID. Refuse held/locked + objects; never infer keys from prefixes. +10. Capture after-counts and a second backup/digest. Restart PostgreSQL, Redis, + MinIO, web, worker, and gateway. Verify health, real login, the four genuine + messages, and all three real agent replies. + +The manifest and receipt contain metadata, never object contents or +credentials. Use synthetic data in tests and documentation. diff --git a/package.json b/package.json index 2e6fbb9..32a999b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "db:migrate": "pnpm --filter @muster/database migrate", "db:bootstrap": "pnpm --filter @muster/database bootstrap", "db:verify-clean": "pnpm --filter @muster/database verify-clean", + "db:verify-audit": "pnpm --filter @muster/database verify:audit", "db:seed": "pnpm --filter @muster/database seed", "alfie:cisa-smoke": "node scripts/alfie-cisa-smoke.mjs", "screenshots": "playwright test tests/screenshots.spec.ts", diff --git a/packages/audit/src/audit.test.ts b/packages/audit/src/audit.test.ts index 23588d9..8fd3a49 100644 --- a/packages/audit/src/audit.test.ts +++ b/packages/audit/src/audit.test.ts @@ -1,5 +1,11 @@ import { describe, expect, it } from "vitest"; -import { hashAuditEvent, verifyAuditChain, type HashableAuditEvent } from "./index"; +import { + hashAuditEvent, + normaliseAuditMetadata, + verifyAuditIntegrity, + verifyAuditChain, + type HashableAuditEvent, +} from "./index"; const base: HashableAuditEvent = { organisationId: "org", @@ -19,8 +25,132 @@ describe("audit chain", () => { it("detects metadata tampering", () => { const event = { ...base, eventHash: hashAuditEvent(base) }; expect(verifyAuditChain([event]).valid).toBe(true); + expect(verifyAuditIntegrity([event])).toMatchObject({ + outcome: "strict-valid", + strict: { valid: true }, + legacyCompatible: { valid: true }, + legacyApprovalIdOmissions: [], + historicalChainRepaired: false, + }); expect( verifyAuditChain([{ ...event, metadata: { safe: false } }]).valid, ).toBe(false); }); + + it("hashes the same JSON representation that PostgreSQL persists", () => { + const metadata = normaliseAuditMetadata({ + operation: "kelpie.timeline.comment", + approvalId: undefined, + nested: { omitted: undefined, kept: true }, + array: [undefined, "kept"], + }); + expect(metadata).toEqual({ + operation: "kelpie.timeline.comment", + nested: { kept: true }, + array: [null, "kept"], + }); + + const event = { ...base, metadata }; + const persisted = JSON.parse(JSON.stringify(event)) as HashableAuditEvent; + expect(hashAuditEvent(event)).toBe(hashAuditEvent(persisted)); + expect( + verifyAuditChain([ + { ...persisted, eventHash: hashAuditEvent(persisted) }, + ]), + ).toEqual({ valid: true }); + }); + + it("reports the legacy undefined approvalId defect without calling it repaired", () => { + const historicalInput = { + ...base, + action: "integration.action.queued", + metadata: { + integrationId: "integration", + operation: "alerts.list", + capability: "alerts.read", + approvalId: undefined, + }, + }; + const historicalEvent = { + ...historicalInput, + metadata: { + integrationId: "integration", + operation: "alerts.list", + capability: "alerts.read", + }, + eventHash: hashAuditEvent(historicalInput), + }; + const followingInput = { + ...base, + sequence: 2, + targetId: "delivery", + previousHash: historicalEvent.eventHash, + }; + const followingEvent = { + ...followingInput, + eventHash: hashAuditEvent(followingInput), + }; + + expect(verifyAuditChain([historicalEvent, followingEvent])).toEqual({ + valid: false, + brokenAt: 1, + }); + expect( + verifyAuditIntegrity([historicalEvent, followingEvent]), + ).toMatchObject({ + outcome: "legacy-compatible-not-strict", + strict: { valid: false, brokenAt: 1 }, + legacyCompatible: { valid: true }, + legacyApprovalIdOmissions: [{ sequence: 1 }], + historicalChainRepaired: false, + }); + }); + + it("does not accept an unexplained hash mismatch as legacy-compatible", () => { + const event = { ...base, eventHash: "0".repeat(64) }; + + expect(verifyAuditIntegrity([event])).toMatchObject({ + outcome: "invalid", + strict: { valid: false, brokenAt: 1 }, + legacyCompatible: { valid: false, brokenAt: 1 }, + legacyApprovalIdOmissions: [], + historicalChainRepaired: false, + }); + }); + + it("rejects sequence gaps even when event hashes link", () => { + const first = { ...base, eventHash: hashAuditEvent(base) }; + const thirdInput = { + ...base, + sequence: 3, + previousHash: first.eventHash, + }; + const third = { ...thirdInput, eventHash: hashAuditEvent(thirdInput) }; + + expect(verifyAuditIntegrity([first, third])).toMatchObject({ + outcome: "invalid", + strict: { valid: false, brokenAt: 3 }, + legacyCompatible: { valid: false, brokenAt: 3 }, + }); + }); + + it("limits legacy compatibility to known integration-action events", () => { + const historicalInput = { + ...base, + action: "unexpected.action", + metadata: { operation: "alerts.list", approvalId: undefined }, + }; + const persisted = { + ...historicalInput, + metadata: { operation: "alerts.list" }, + eventHash: hashAuditEvent(historicalInput), + }; + + expect(verifyAuditIntegrity([persisted])).toMatchObject({ + outcome: "invalid", + strict: { valid: false, brokenAt: 1 }, + legacyCompatible: { valid: false, brokenAt: 1 }, + legacyApprovalIdOmissions: [], + }); + }); }); diff --git a/packages/audit/src/index.ts b/packages/audit/src/index.ts index 785d246..cf22795 100644 --- a/packages/audit/src/index.ts +++ b/packages/audit/src/index.ts @@ -14,6 +14,28 @@ export interface HashableAuditEvent { createdAt: string; } +export type AuditChainVerification = + { valid: true } | { valid: false; brokenAt: number }; + +export interface AuditLegacyCompatibilityMatch { + sequence: number; +} + +export interface AuditIntegrityReport { + outcome: "strict-valid" | "legacy-compatible-not-strict" | "invalid"; + strict: AuditChainVerification; + legacyCompatible: AuditChainVerification; + legacyApprovalIdOmissions: ReadonlyArray; + historicalChainRepaired: false; + attestation: string; +} + +type PersistedAuditEvent = HashableAuditEvent & { eventHash: string }; +const legacyApprovalIdActions = new Set([ + "integration.action.queued", + "integration.action.succeeded", +]); + function canonical(value: unknown): string { if (value === null || typeof value !== "object") return JSON.stringify(value); if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; @@ -27,19 +49,116 @@ export function hashAuditEvent(event: HashableAuditEvent): string { return createHash("sha256").update(canonical(event)).digest("hex"); } -export function verifyAuditChain( - events: ReadonlyArray, -): { valid: boolean; brokenAt?: number } { +export function normaliseAuditMetadata(metadata: unknown): unknown { + const serialised = JSON.stringify(metadata ?? {}); + if (serialised === undefined) return {}; + return JSON.parse(serialised) as unknown; +} + +function isLegacyApprovalIdOmission(event: PersistedAuditEvent): boolean { + if ( + !legacyApprovalIdActions.has(event.action) || + event.metadata === null || + typeof event.metadata !== "object" || + Array.isArray(event.metadata) || + Object.hasOwn(event.metadata, "approvalId") + ) { + return false; + } + const { eventHash, ...hashable } = event; + return ( + hashAuditEvent({ + ...hashable, + metadata: { ...event.metadata, approvalId: undefined }, + }) === eventHash + ); +} + +function verify( + events: ReadonlyArray, + allowLegacyApprovalIdOmission: boolean, +): { + verification: AuditChainVerification; + legacyApprovalIdOmissions: AuditLegacyCompatibilityMatch[]; +} { + const legacyApprovalIdOmissions: AuditLegacyCompatibilityMatch[] = []; let previousHash = "0".repeat(64); + let expectedSequence = 1; for (const event of events) { - if (event.previousHash !== previousHash) { - return { valid: false, brokenAt: event.sequence }; + if ( + event.sequence !== expectedSequence || + event.previousHash !== previousHash + ) { + return { + verification: { valid: false, brokenAt: event.sequence }, + legacyApprovalIdOmissions, + }; } const { eventHash, ...hashable } = event; if (hashAuditEvent(hashable) !== eventHash) { - return { valid: false, brokenAt: event.sequence }; + if ( + !allowLegacyApprovalIdOmission || + !isLegacyApprovalIdOmission(event) + ) { + return { + verification: { valid: false, brokenAt: event.sequence }, + legacyApprovalIdOmissions, + }; + } + legacyApprovalIdOmissions.push({ sequence: event.sequence }); } previousHash = eventHash; + expectedSequence += 1; } - return { valid: true }; + return { verification: { valid: true }, legacyApprovalIdOmissions }; +} + +export function verifyAuditChain( + events: ReadonlyArray, +): AuditChainVerification { + return verify(events, false).verification; +} + +export function verifyAuditIntegrity( + events: ReadonlyArray, +): AuditIntegrityReport { + const strict = verify(events, false).verification; + const legacy = verify(events, true); + + if (strict.valid) { + return { + outcome: "strict-valid", + strict, + legacyCompatible: legacy.verification, + legacyApprovalIdOmissions: [], + historicalChainRepaired: false, + attestation: + "Strict audit-chain verification passed. No historical event was repaired or changed.", + }; + } + + if ( + legacy.verification.valid && + legacy.legacyApprovalIdOmissions.length > 0 + ) { + return { + outcome: "legacy-compatible-not-strict", + strict, + legacyCompatible: legacy.verification, + legacyApprovalIdOmissions: legacy.legacyApprovalIdOmissions, + historicalChainRepaired: false, + attestation: + "A known pre-normalisation undefined approvalId hash is reproducible. Strict verification still fails; this does not repair or change immutable audit history.", + }; + } + + return { + outcome: "invalid", + strict, + legacyCompatible: legacy.verification, + legacyApprovalIdOmissions: legacy.legacyApprovalIdOmissions, + historicalChainRepaired: false, + attestation: + "Strict verification failed and no known legacy compatibility reconstruction explains the chain. Preserve immutable history and investigate before attesting recovery.", + }; } diff --git a/packages/authz/src/index.ts b/packages/authz/src/index.ts index 0d296d8..8302b77 100644 --- a/packages/authz/src/index.ts +++ b/packages/authz/src/index.ts @@ -259,6 +259,14 @@ export const actionApprovalPolicy = { approvalCount: 1, capability: "workflows.approve", }, + "maintenance.synthetic-cleanup": { + approvalCount: 1, + capability: "administration.manage", + }, + "maintenance.synthetic-cleanup.object-delete-retry": { + approvalCount: 1, + capability: "administration.manage", + }, "evidence.delete": { prohibited: true }, } as const satisfies Record< string, diff --git a/packages/database/migrations/0017_daily_dexter_bennett.sql b/packages/database/migrations/0017_daily_dexter_bennett.sql new file mode 100644 index 0000000..5ba743a --- /dev/null +++ b/packages/database/migrations/0017_daily_dexter_bennett.sql @@ -0,0 +1,6 @@ +ALTER TABLE "hunt_runs" ADD COLUMN "archived_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "integration_records" ADD COLUMN "archived_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "report_manifests" ADD COLUMN "archived_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "report_schedules" ADD COLUMN "archived_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "research_watchlists" ADD COLUMN "archived_at" timestamp with time zone;--> statement-breakpoint +ALTER TABLE "tasks" ADD COLUMN "archived_at" timestamp with time zone; \ No newline at end of file diff --git a/packages/database/migrations/0018_watery_morg.sql b/packages/database/migrations/0018_watery_morg.sql new file mode 100644 index 0000000..83b9b89 --- /dev/null +++ b/packages/database/migrations/0018_watery_morg.sql @@ -0,0 +1,37 @@ +CREATE TABLE "synthetic_cleanup_receipts" ( + "manifest_id" uuid PRIMARY KEY NOT NULL, + "organisation_id" uuid NOT NULL, + "approval_id" uuid NOT NULL, + "maintenance_actor_id" uuid NOT NULL, + "manifest_digest" text NOT NULL, + "manifest" jsonb NOT NULL, + "candidate_counts" jsonb NOT NULL, + "pre_digests" jsonb NOT NULL, + "post_digests" jsonb NOT NULL, + "object_storage_objects" jsonb DEFAULT '[]'::jsonb NOT NULL, + "trace_id" text NOT NULL, + "applied_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "synthetic_cleanup_receipts" ADD CONSTRAINT "synthetic_cleanup_receipts_organisation_id_organisations_id_fk" FOREIGN KEY ("organisation_id") REFERENCES "public"."organisations"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "synthetic_cleanup_receipts" ADD CONSTRAINT "synthetic_cleanup_receipts_approval_id_approvals_id_fk" FOREIGN KEY ("approval_id") REFERENCES "public"."approvals"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "synthetic_cleanup_receipts" ADD CONSTRAINT "synthetic_cleanup_receipts_maintenance_actor_id_actors_id_fk" FOREIGN KEY ("maintenance_actor_id") REFERENCES "public"."actors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "synthetic_cleanup_receipts_approval_unique" ON "synthetic_cleanup_receipts" USING btree ("approval_id");--> statement-breakpoint +CREATE UNIQUE INDEX "synthetic_cleanup_receipts_org_digest_unique" ON "synthetic_cleanup_receipts" USING btree ("organisation_id","manifest_digest");--> statement-breakpoint +CREATE FUNCTION "prevent_append_only_mutation"() +RETURNS trigger +LANGUAGE plpgsql +AS $$ +BEGIN + RAISE EXCEPTION '% is append-only', TG_TABLE_NAME; +END; +$$;--> statement-breakpoint +CREATE TRIGGER "synthetic_cleanup_receipts_append_only" +BEFORE UPDATE OR DELETE ON "synthetic_cleanup_receipts" +FOR EACH ROW EXECUTE FUNCTION "prevent_append_only_mutation"();--> statement-breakpoint +CREATE TRIGGER "audit_events_append_only" +BEFORE UPDATE OR DELETE ON "audit_events" +FOR EACH ROW EXECUTE FUNCTION "prevent_append_only_mutation"();--> statement-breakpoint +CREATE TRIGGER "message_revisions_append_only" +BEFORE UPDATE OR DELETE ON "message_revisions" +FOR EACH ROW EXECUTE FUNCTION "prevent_append_only_mutation"(); diff --git a/packages/database/migrations/0019_fuzzy_zemo.sql b/packages/database/migrations/0019_fuzzy_zemo.sql new file mode 100644 index 0000000..799306e --- /dev/null +++ b/packages/database/migrations/0019_fuzzy_zemo.sql @@ -0,0 +1,42 @@ +CREATE TABLE "synthetic_artifact_provenance" ( + "id" uuid PRIMARY KEY NOT NULL, + "organisation_id" uuid NOT NULL, + "artifact_table" text NOT NULL, + "artifact_id" uuid NOT NULL, + "source_kind" text NOT NULL, + "source_reference" text NOT NULL, + "recorded_by_actor_id" uuid NOT NULL, + "recorded_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "synthetic_artifact_provenance_table_check" CHECK ("synthetic_artifact_provenance"."artifact_table" in ('rooms','tasks','hunts','integrations','researchWatchlists','reportManifests','reportSchedules','messages','evidence','agentMemories','actors')), + CONSTRAINT "synthetic_artifact_provenance_source_check" CHECK ("synthetic_artifact_provenance"."source_kind" in ('seed_fixture','mock_runtime','test_fixture','legacy_live_proof')) +); +--> statement-breakpoint +CREATE TABLE "synthetic_cleanup_object_deletion_attempts" ( + "id" uuid PRIMARY KEY NOT NULL, + "manifest_id" uuid NOT NULL, + "organisation_id" uuid NOT NULL, + "evidence_id" uuid NOT NULL, + "version_id" text NOT NULL, + "authorization_approval_id" uuid NOT NULL, + "result" text NOT NULL, + "error_code" text, + "attempted_by_actor_id" uuid NOT NULL, + "trace_id" text NOT NULL, + "attempted_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "synthetic_cleanup_object_attempts_result_check" CHECK ("synthetic_cleanup_object_deletion_attempts"."result" in ('started','succeeded','failed','observed_missing')) +); +--> statement-breakpoint +ALTER TABLE "synthetic_artifact_provenance" ADD CONSTRAINT "synthetic_artifact_provenance_organisation_id_organisations_id_fk" FOREIGN KEY ("organisation_id") REFERENCES "public"."organisations"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "synthetic_artifact_provenance" ADD CONSTRAINT "synthetic_artifact_provenance_recorded_by_actor_id_actors_id_fk" FOREIGN KEY ("recorded_by_actor_id") REFERENCES "public"."actors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "synthetic_cleanup_object_deletion_attempts" ADD CONSTRAINT "synthetic_cleanup_object_deletion_attempts_manifest_id_synthetic_cleanup_receipts_manifest_id_fk" FOREIGN KEY ("manifest_id") REFERENCES "public"."synthetic_cleanup_receipts"("manifest_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "synthetic_cleanup_object_deletion_attempts" ADD CONSTRAINT "synthetic_cleanup_object_deletion_attempts_organisation_id_organisations_id_fk" FOREIGN KEY ("organisation_id") REFERENCES "public"."organisations"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "synthetic_cleanup_object_deletion_attempts" ADD CONSTRAINT "synthetic_cleanup_object_deletion_attempts_authorization_approval_id_approvals_id_fk" FOREIGN KEY ("authorization_approval_id") REFERENCES "public"."approvals"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "synthetic_cleanup_object_deletion_attempts" ADD CONSTRAINT "synthetic_cleanup_object_deletion_attempts_attempted_by_actor_id_actors_id_fk" FOREIGN KEY ("attempted_by_actor_id") REFERENCES "public"."actors"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "synthetic_artifact_provenance_artifact_unique" ON "synthetic_artifact_provenance" USING btree ("organisation_id","artifact_table","artifact_id");--> statement-breakpoint +CREATE INDEX "synthetic_cleanup_object_attempts_manifest_idx" ON "synthetic_cleanup_object_deletion_attempts" USING btree ("organisation_id","manifest_id","attempted_at");--> statement-breakpoint +CREATE TRIGGER "synthetic_artifact_provenance_append_only" +BEFORE UPDATE OR DELETE ON "synthetic_artifact_provenance" +FOR EACH ROW EXECUTE FUNCTION "prevent_append_only_mutation"();--> statement-breakpoint +CREATE TRIGGER "synthetic_cleanup_object_attempts_append_only" +BEFORE UPDATE OR DELETE ON "synthetic_cleanup_object_deletion_attempts" +FOR EACH ROW EXECUTE FUNCTION "prevent_append_only_mutation"(); diff --git a/packages/database/migrations/meta/0017_snapshot.json b/packages/database/migrations/meta/0017_snapshot.json new file mode 100644 index 0000000..ace2a3a --- /dev/null +++ b/packages/database/migrations/meta/0017_snapshot.json @@ -0,0 +1,11199 @@ +{ + "id": "3f61ffb8-af0d-45ef-a0be-7f325899949f", + "prevId": "4d1b8c61-00dd-4090-a541-85aed810ed71", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.actors": { + "name": "actors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "actor_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "identity_reference": { + "name": "identity_reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capability_assignments": { + "name": "capability_assignments", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "actors_org_type_idx": { + "name": "actors_org_type_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "actors_org_identity_unique": { + "name": "actors_org_identity_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "identity_reference", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"actors\".\"identity_reference\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "actors_organisation_id_organisations_id_fk": { + "name": "actors_organisation_id_organisations_id_fk", + "tableFrom": "actors", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_definitions": { + "name": "agent_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runtime": { + "name": "runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_actor_id": { + "name": "owner_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "system_prompt_version": { + "name": "system_prompt_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allowed_tools": { + "name": "allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "allowed_rooms": { + "name": "allowed_rooms", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "capability_requirements": { + "name": "capability_requirements", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "maximum_runtime_seconds": { + "name": "maximum_runtime_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 300 + }, + "maximum_token_budget": { + "name": "maximum_token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20000 + }, + "maximum_cost_cents": { + "name": "maximum_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 500 + }, + "data_classification_allowance": { + "name": "data_classification_allowance", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"internal\"]'::jsonb" + }, + "approval_requirements": { + "name": "approval_requirements", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "requested_permission_mode": { + "name": "requested_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'read_only'" + }, + "kill_switch": { + "name": "kill_switch", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_definitions_org_name_unique": { + "name": "agent_definitions_org_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_definitions_organisation_id_organisations_id_fk": { + "name": "agent_definitions_organisation_id_organisations_id_fk", + "tableFrom": "agent_definitions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_definitions_owner_actor_id_actors_id_fk": { + "name": "agent_definitions_owner_actor_id_actors_id_fk", + "tableFrom": "agent_definitions", + "tableTo": "actors", + "columnsFrom": [ + "owner_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_definitions_requested_permission_check": { + "name": "agent_definitions_requested_permission_check", + "value": "\"agent_definitions\".\"requested_permission_mode\" in ('read_only','approval_gated')" + } + }, + "isRLSEnabled": false + }, + "public.agent_memories": { + "name": "agent_memories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "supersedes_memory_id": { + "name": "supersedes_memory_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reviewed_by_actor_id": { + "name": "reviewed_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_memories_org_agent_idx": { + "name": "agent_memories_org_agent_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_memories_organisation_id_organisations_id_fk": { + "name": "agent_memories_organisation_id_organisations_id_fk", + "tableFrom": "agent_memories", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_agent_id_agent_definitions_id_fk": { + "name": "agent_memories_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_memories", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_source_run_id_agent_runs_id_fk": { + "name": "agent_memories_source_run_id_agent_runs_id_fk", + "tableFrom": "agent_memories", + "tableTo": "agent_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_reviewed_by_actor_id_actors_id_fk": { + "name": "agent_memories_reviewed_by_actor_id_actors_id_fk", + "tableFrom": "agent_memories", + "tableTo": "actors", + "columnsFrom": [ + "reviewed_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_memories_kind_check": { + "name": "agent_memories_kind_check", + "value": "\"agent_memories\".\"kind\" in ('fact','preference','lesson','failure','procedure_hint')" + }, + "agent_memories_status_check": { + "name": "agent_memories_status_check", + "value": "\"agent_memories\".\"status\" in ('active','superseded','expired','rejected')" + }, + "agent_memories_confidence_check": { + "name": "agent_memories_confidence_check", + "value": "\"agent_memories\".\"confidence\" between 0 and 100" + } + }, + "isRLSEnabled": false + }, + "public.agent_readiness_snapshots": { + "name": "agent_readiness_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "process_identity": { + "name": "process_identity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "gateway_state": { + "name": "gateway_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authentication_state": { + "name": "authentication_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "observer_state": { + "name": "observer_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle_evidence_state": { + "name": "lifecycle_evidence_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle_state": { + "name": "lifecycle_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "capability_state": { + "name": "capability_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_state": { + "name": "tool_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_state": { + "name": "permission_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_runtime": { + "name": "reported_runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_provider": { + "name": "reported_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_model": { + "name": "reported_model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_capabilities": { + "name": "input_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "output_capabilities": { + "name": "output_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "available_commands": { + "name": "available_commands", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "tool_sources": { + "name": "tool_sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "tool_risk_classes": { + "name": "tool_risk_classes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "requested_permission_mode": { + "name": "requested_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_permission_mode": { + "name": "effective_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "limitations": { + "name": "limitations", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_readiness_org_agent_verified_idx": { + "name": "agent_readiness_org_agent_verified_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "verified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_readiness_org_process_verified_idx": { + "name": "agent_readiness_org_process_verified_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "process_identity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "verified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_readiness_snapshots_organisation_id_organisations_id_fk": { + "name": "agent_readiness_snapshots_organisation_id_organisations_id_fk", + "tableFrom": "agent_readiness_snapshots", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_readiness_snapshots_agent_id_agent_definitions_id_fk": { + "name": "agent_readiness_snapshots_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_readiness_snapshots", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_readiness_evidence_states_check": { + "name": "agent_readiness_evidence_states_check", + "value": "\"agent_readiness_snapshots\".\"gateway_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"authentication_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"observer_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"lifecycle_evidence_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"capability_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"tool_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"permission_state\" in ('reported','unavailable','unknown')" + }, + "agent_readiness_lifecycle_state_check": { + "name": "agent_readiness_lifecycle_state_check", + "value": "\"agent_readiness_snapshots\".\"lifecycle_state\" in ('idle','running','stopped','failed','unknown')" + }, + "agent_readiness_permission_modes_check": { + "name": "agent_readiness_permission_modes_check", + "value": "\"agent_readiness_snapshots\".\"requested_permission_mode\" in ('read_only','approval_gated','unknown')\n and \"agent_readiness_snapshots\".\"effective_permission_mode\" in ('read_only','approval_gated','unknown')" + } + }, + "isRLSEnabled": false + }, + "public.agent_run_events": { + "name": "agent_run_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_run_events_org_run_idx": { + "name": "agent_run_events_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_events_organisation_id_organisations_id_fk": { + "name": "agent_run_events_organisation_id_organisations_id_fk", + "tableFrom": "agent_run_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_run_events_run_id_agent_runs_id_fk": { + "name": "agent_run_events_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_events", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_run_sources": { + "name": "agent_run_sources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_run_sources_org_run_unique": { + "name": "agent_run_sources_org_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_run_sources_org_run_idx": { + "name": "agent_run_sources_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_sources_organisation_id_organisations_id_fk": { + "name": "agent_run_sources_organisation_id_organisations_id_fk", + "tableFrom": "agent_run_sources", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_run_sources_run_id_agent_runs_id_fk": { + "name": "agent_run_sources_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_sources", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_runs": { + "name": "agent_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workflow_run_id": { + "name": "workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "request": { + "name": "request", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "progress": { + "name": "progress", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deadline_at": { + "name": "deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancellation_requested_at": { + "name": "cancellation_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "input_hash": { + "name": "input_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt_hash": { + "name": "prompt_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output_hash": { + "name": "output_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output_schema": { + "name": "output_schema", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "runtime": { + "name": "runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "maximum_runtime_seconds": { + "name": "maximum_runtime_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 300 + }, + "maximum_token_budget": { + "name": "maximum_token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20000 + }, + "maximum_cost_cents": { + "name": "maximum_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 500 + }, + "token_usage": { + "name": "token_usage", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "estimated_cost_cents": { + "name": "estimated_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tool_call_count": { + "name": "tool_call_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "diagnostics": { + "name": "diagnostics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cancellation_reason": { + "name": "cancellation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "structured_output": { + "name": "structured_output", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "agent_runs_org_idempotency_unique": { + "name": "agent_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_runs_org_status_idx": { + "name": "agent_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_runs_recovery_idx": { + "name": "agent_runs_recovery_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lease_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_runs_agent_id_agent_definitions_id_fk": { + "name": "agent_runs_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_runs", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_organisation_id_organisations_id_fk": { + "name": "agent_runs_organisation_id_organisations_id_fk", + "tableFrom": "agent_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_room_id_rooms_id_fk": { + "name": "agent_runs_room_id_rooms_id_fk", + "tableFrom": "agent_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_investigation_id_investigations_id_fk": { + "name": "agent_runs_investigation_id_investigations_id_fk", + "tableFrom": "agent_runs", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_requested_by_actor_id_actors_id_fk": { + "name": "agent_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "agent_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_skill_evaluations": { + "name": "agent_skill_evaluations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_version_id": { + "name": "skill_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "evaluator_actor_id": { + "name": "evaluator_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "suite": { + "name": "suite", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "passed": { + "name": "passed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "score": { + "name": "score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "baseline_score": { + "name": "baseline_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "regressions": { + "name": "regressions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skill_evaluations_version_idx": { + "name": "agent_skill_evaluations_version_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skill_evaluations_organisation_id_organisations_id_fk": { + "name": "agent_skill_evaluations_organisation_id_organisations_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_evaluations_skill_version_id_agent_skill_versions_id_fk": { + "name": "agent_skill_evaluations_skill_version_id_agent_skill_versions_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "agent_skill_versions", + "columnsFrom": [ + "skill_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_evaluations_evaluator_actor_id_actors_id_fk": { + "name": "agent_skill_evaluations_evaluator_actor_id_actors_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "actors", + "columnsFrom": [ + "evaluator_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skill_evaluations_score_check": { + "name": "agent_skill_evaluations_score_check", + "value": "\"agent_skill_evaluations\".\"score\" between 0 and 100" + } + }, + "isRLSEnabled": false + }, + "public.agent_skill_versions": { + "name": "agent_skill_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_id": { + "name": "skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "based_on_version_id": { + "name": "based_on_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "change_rationale": { + "name": "change_rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "required_capabilities": { + "name": "required_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "allowed_tools": { + "name": "allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'proposed'" + }, + "approved_by_actor_id": { + "name": "approved_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skill_versions_skill_version_unique": { + "name": "agent_skill_versions_skill_version_unique", + "columns": [ + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_skill_versions_content_hash_unique": { + "name": "agent_skill_versions_content_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "content_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skill_versions_organisation_id_organisations_id_fk": { + "name": "agent_skill_versions_organisation_id_organisations_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_skill_id_agent_skills_id_fk": { + "name": "agent_skill_versions_skill_id_agent_skills_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "agent_skills", + "columnsFrom": [ + "skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_source_run_id_agent_runs_id_fk": { + "name": "agent_skill_versions_source_run_id_agent_runs_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "agent_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_approved_by_actor_id_actors_id_fk": { + "name": "agent_skill_versions_approved_by_actor_id_actors_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "actors", + "columnsFrom": [ + "approved_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skill_versions_state_check": { + "name": "agent_skill_versions_state_check", + "value": "\"agent_skill_versions\".\"state\" in ('proposed','evaluating','approved','rejected','published','rolled_back')" + } + }, + "isRLSEnabled": false + }, + "public.agent_skills": { + "name": "agent_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_key": { + "name": "skill_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "active_version_id": { + "name": "active_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skills_org_agent_key_unique": { + "name": "agent_skills_org_agent_key_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skills_organisation_id_organisations_id_fk": { + "name": "agent_skills_organisation_id_organisations_id_fk", + "tableFrom": "agent_skills", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skills_agent_id_agent_definitions_id_fk": { + "name": "agent_skills_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_skills", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skills_created_by_actor_id_actors_id_fk": { + "name": "agent_skills_created_by_actor_id_actors_id_fk", + "tableFrom": "agent_skills", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skills_status_check": { + "name": "agent_skills_status_check", + "value": "\"agent_skills\".\"status\" in ('draft','evaluating','published','retired')" + } + }, + "isRLSEnabled": false + }, + "public.agent_tool_calls": { + "name": "agent_tool_calls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "capability": { + "name": "capability", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "arguments_hash": { + "name": "arguments_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "result_hash": { + "name": "result_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "agent_tool_calls_org_run_idx": { + "name": "agent_tool_calls_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_tool_calls_organisation_id_organisations_id_fk": { + "name": "agent_tool_calls_organisation_id_organisations_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_tool_calls_run_id_agent_runs_id_fk": { + "name": "agent_tool_calls_run_id_agent_runs_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_tool_calls_approval_id_approvals_id_fk": { + "name": "agent_tool_calls_approval_id_approvals_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.alerts": { + "name": "alerts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_product": { + "name": "source_product", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_instance": { + "name": "source_instance", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_reference": { + "name": "external_reference", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "alert_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'new'" + }, + "rule_name": { + "name": "rule_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rule_id": { + "name": "rule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "assigned_actor_id": { + "name": "assigned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "entities": { + "name": "entities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "observables": { + "name": "observables", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "raw_reference_metadata": { + "name": "raw_reference_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kelpie_case_id": { + "name": "kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "correlation_key": { + "name": "correlation_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "alerts_org_source_ref_unique": { + "name": "alerts_org_source_ref_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_product", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_instance", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_reference", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_org_dedupe_unique": { + "name": "alerts_org_dedupe_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_org_queue_idx": { + "name": "alerts_org_queue_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "severity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "received_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_search_idx": { + "name": "alerts_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"description\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "alerts_organisation_id_organisations_id_fk": { + "name": "alerts_organisation_id_organisations_id_fk", + "tableFrom": "alerts", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "alerts_assigned_actor_id_actors_id_fk": { + "name": "alerts_assigned_actor_id_actors_id_fk", + "tableFrom": "alerts", + "tableTo": "actors", + "columnsFrom": [ + "assigned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "alerts_room_id_rooms_id_fk": { + "name": "alerts_room_id_rooms_id_fk", + "tableFrom": "alerts", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.approvals": { + "name": "approvals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requesting_actor_id": { + "name": "requesting_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action_type": { + "name": "action_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "risk_summary": { + "name": "risk_summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "required_capability": { + "name": "required_capability", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required_approval_count": { + "name": "required_approval_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "approval_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "decisions": { + "name": "decisions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "decision_at": { + "name": "decision_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "approvals_org_idempotency_unique": { + "name": "approvals_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "approvals_org_status_idx": { + "name": "approvals_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "approvals_organisation_id_organisations_id_fk": { + "name": "approvals_organisation_id_organisations_id_fk", + "tableFrom": "approvals", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "approvals_requesting_actor_id_actors_id_fk": { + "name": "approvals_requesting_actor_id_actors_id_fk", + "tableFrom": "approvals", + "tableTo": "actors", + "columnsFrom": [ + "requesting_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_events": { + "name": "audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "actor_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previous_hash": { + "name": "previous_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_hash": { + "name": "event_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_org_sequence_unique": { + "name": "audit_org_sequence_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_org_hash_unique": { + "name": "audit_org_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_org_target_idx": { + "name": "audit_org_target_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_events_organisation_id_organisations_id_fk": { + "name": "audit_events_organisation_id_organisations_id_fk", + "tableFrom": "audit_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "audit_events_actor_id_actors_id_fk": { + "name": "audit_events_actor_id_actors_id_fk", + "tableFrom": "audit_events", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_account": { + "name": "auth_account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_accounts_user_idx": { + "name": "auth_accounts_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_accounts_provider_unique": { + "name": "auth_accounts_provider_unique", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_account_user_id_auth_user_id_fk": { + "name": "auth_account_user_id_auth_user_id_fk", + "tableFrom": "auth_account", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_passkey": { + "name": "auth_passkey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_type": { + "name": "device_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backed_up": { + "name": "backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "aaguid": { + "name": "aaguid", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_passkey_user_idx": { + "name": "auth_passkey_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_passkey_user_id_auth_user_id_fk": { + "name": "auth_passkey_user_id_auth_user_id_fk", + "tableFrom": "auth_passkey", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_passkey_credential_id_unique": { + "name": "auth_passkey_credential_id_unique", + "nullsNotDistinct": false, + "columns": [ + "credential_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_session": { + "name": "auth_session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_sessions_user_idx": { + "name": "auth_sessions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_session_user_id_auth_user_id_fk": { + "name": "auth_session_user_id_auth_user_id_fk", + "tableFrom": "auth_session", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_session_token_unique": { + "name": "auth_session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_two_factor": { + "name": "auth_two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "failed_verification_count": { + "name": "failed_verification_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "locked_until": { + "name": "locked_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_two_factor_user_unique": { + "name": "auth_two_factor_user_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_two_factor_user_id_auth_user_id_fk": { + "name": "auth_two_factor_user_id_auth_user_id_fk", + "tableFrom": "auth_two_factor", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_user": { + "name": "auth_user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_user_email_unique": { + "name": "auth_user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_verification": { + "name": "auth_verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_verification_identifier_idx": { + "name": "auth_verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.decisions": { + "name": "decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "decision": { + "name": "decision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decision_maker_actor_id": { + "name": "decision_maker_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "alternatives_considered": { + "name": "alternatives_considered", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_workflow_run_id": { + "name": "related_workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decisions_org_investigation_idx": { + "name": "decisions_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "related_investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decisions_organisation_id_organisations_id_fk": { + "name": "decisions_organisation_id_organisations_id_fk", + "tableFrom": "decisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_decision_maker_actor_id_actors_id_fk": { + "name": "decisions_decision_maker_actor_id_actors_id_fk", + "tableFrom": "decisions", + "tableTo": "actors", + "columnsFrom": [ + "decision_maker_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_related_investigation_id_investigations_id_fk": { + "name": "decisions_related_investigation_id_investigations_id_fk", + "tableFrom": "decisions", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.evidence": { + "name": "evidence", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_by_actor_id": { + "name": "uploaded_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "related_room_id": { + "name": "related_room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_timestamp": { + "name": "original_timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scan_state": { + "name": "scan_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "retention_state": { + "name": "retention_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "legal_hold": { + "name": "legal_hold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "object_lock_metadata": { + "name": "object_lock_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "evidence_org_hash_unique": { + "name": "evidence_org_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "evidence_search_idx": { + "name": "evidence_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"file_name\" || ' ' || \"source\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "evidence_organisation_id_organisations_id_fk": { + "name": "evidence_organisation_id_organisations_id_fk", + "tableFrom": "evidence", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_uploaded_by_actor_id_actors_id_fk": { + "name": "evidence_uploaded_by_actor_id_actors_id_fk", + "tableFrom": "evidence", + "tableTo": "actors", + "columnsFrom": [ + "uploaded_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_related_room_id_rooms_id_fk": { + "name": "evidence_related_room_id_rooms_id_fk", + "tableFrom": "evidence", + "tableTo": "rooms", + "columnsFrom": [ + "related_room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_related_investigation_id_investigations_id_fk": { + "name": "evidence_related_investigation_id_investigations_id_fk", + "tableFrom": "evidence", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.findings": { + "name": "findings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "supporting_evidence": { + "name": "supporting_evidence", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_entities": { + "name": "related_entities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_observables": { + "name": "related_observables", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "recommended_action": { + "name": "recommended_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_provenance": { + "name": "agent_provenance", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "human_reviewed_at": { + "name": "human_reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "findings_org_investigation_idx": { + "name": "findings_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "findings_search_idx": { + "name": "findings_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"summary\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "findings_organisation_id_organisations_id_fk": { + "name": "findings_organisation_id_organisations_id_fk", + "tableFrom": "findings", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "findings_investigation_id_investigations_id_fk": { + "name": "findings_investigation_id_investigations_id_fk", + "tableFrom": "findings", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "findings_created_by_actor_id_actors_id_fk": { + "name": "findings_created_by_actor_id_actors_id_fk", + "tableFrom": "findings", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hunt_queries": { + "name": "hunt_queries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "hunt_id": { + "name": "hunt_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "query_run_id": { + "name": "query_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_key": { + "name": "source_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "hunt_queries_org_query_run_unique": { + "name": "hunt_queries_org_query_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "query_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_queries_org_hunt_sequence_unique": { + "name": "hunt_queries_org_hunt_sequence_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hunt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_queries_org_hunt_idx": { + "name": "hunt_queries_org_hunt_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hunt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hunt_queries_organisation_id_organisations_id_fk": { + "name": "hunt_queries_organisation_id_organisations_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_hunt_id_hunt_runs_id_fk": { + "name": "hunt_queries_hunt_id_hunt_runs_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "hunt_runs", + "columnsFrom": [ + "hunt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_integration_id_integration_records_id_fk": { + "name": "hunt_queries_integration_id_integration_records_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_template_id_integration_query_templates_id_fk": { + "name": "hunt_queries_template_id_integration_query_templates_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_query_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_query_run_id_integration_query_runs_id_fk": { + "name": "hunt_queries_query_run_id_integration_query_runs_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_query_runs", + "columnsFrom": [ + "query_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hunt_runs": { + "name": "hunt_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_message_id": { + "name": "source_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "linked_case_id": { + "name": "linked_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "training_mode": { + "name": "training_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "plan": { + "name": "plan", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'planned'" + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "hunt_runs_org_idempotency_unique": { + "name": "hunt_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_runs_org_agent_run_unique": { + "name": "hunt_runs_org_agent_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_runs_org_status_idx": { + "name": "hunt_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hunt_runs_organisation_id_organisations_id_fk": { + "name": "hunt_runs_organisation_id_organisations_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_agent_run_id_agent_runs_id_fk": { + "name": "hunt_runs_agent_run_id_agent_runs_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_task_id_tasks_id_fk": { + "name": "hunt_runs_task_id_tasks_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "tasks", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_source_message_id_messages_id_fk": { + "name": "hunt_runs_source_message_id_messages_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "messages", + "columnsFrom": [ + "source_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_room_id_rooms_id_fk": { + "name": "hunt_runs_room_id_rooms_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_requested_by_actor_id_actors_id_fk": { + "name": "hunt_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_approval_id_approvals_id_fk": { + "name": "hunt_runs_approval_id_approvals_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "hunt_runs_status_check": { + "name": "hunt_runs_status_check", + "value": "\"hunt_runs\".\"status\" in ('planned','awaiting_approval','querying','analysing','completed','failed','cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.hypotheses": { + "name": "hypotheses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "statement": { + "name": "statement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 50 + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "supporting_finding_ids": { + "name": "supporting_finding_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "contradicting_finding_ids": { + "name": "contradicting_finding_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "hypotheses_org_investigation_idx": { + "name": "hypotheses_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hypotheses_organisation_id_organisations_id_fk": { + "name": "hypotheses_organisation_id_organisations_id_fk", + "tableFrom": "hypotheses", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hypotheses_investigation_id_investigations_id_fk": { + "name": "hypotheses_investigation_id_investigations_id_fk", + "tableFrom": "hypotheses", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hypotheses_created_by_actor_id_actors_id_fk": { + "name": "hypotheses_created_by_actor_id_actors_id_fk", + "tableFrom": "hypotheses", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.idempotency_records": { + "name": "idempotency_records", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "response_status": { + "name": "response_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "response_body": { + "name": "response_body", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idempotency_expiry_idx": { + "name": "idempotency_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "idempotency_records_organisation_id_organisations_id_fk": { + "name": "idempotency_records_organisation_id_organisations_id_fk", + "tableFrom": "idempotency_records", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "idempotency_records_organisation_id_scope_key_pk": { + "name": "idempotency_records_organisation_id_scope_key_pk", + "columns": [ + "organisation_id", + "scope", + "key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_connector_credentials": { + "name": "integration_connector_credentials", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "encrypted_credential": { + "name": "encrypted_credential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "envelope_version": { + "name": "envelope_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'v1'" + }, + "rotation_version": { + "name": "rotation_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "rotated_by_actor_id": { + "name": "rotated_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_credentials_org_idx": { + "name": "integration_credentials_org_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_connector_credentials_organisation_id_organisations_id_fk": { + "name": "integration_connector_credentials_organisation_id_organisations_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_connector_credentials_integration_id_integration_records_id_fk": { + "name": "integration_connector_credentials_integration_id_integration_records_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_connector_credentials_rotated_by_actor_id_actors_id_fk": { + "name": "integration_connector_credentials_rotated_by_actor_id_actors_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "actors", + "columnsFrom": [ + "rotated_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_deliveries": { + "name": "integration_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "request_metadata": { + "name": "request_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "response_metadata": { + "name": "response_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_delivery_org_idempotency_unique": { + "name": "integration_delivery_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_deliveries_organisation_id_organisations_id_fk": { + "name": "integration_deliveries_organisation_id_organisations_id_fk", + "tableFrom": "integration_deliveries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_deliveries_integration_id_integration_records_id_fk": { + "name": "integration_deliveries_integration_id_integration_records_id_fk", + "tableFrom": "integration_deliveries", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_entities": { + "name": "integration_entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "posture": { + "name": "posture", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "integration_entities_org_external_unique": { + "name": "integration_entities_org_external_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_entities_organisation_id_organisations_id_fk": { + "name": "integration_entities_organisation_id_organisations_id_fk", + "tableFrom": "integration_entities", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_entities_integration_id_integration_records_id_fk": { + "name": "integration_entities_integration_id_integration_records_id_fk", + "tableFrom": "integration_entities", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_query_runs": { + "name": "integration_query_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "input": { + "name": "input", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "request_metadata": { + "name": "request_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "response_metadata": { + "name": "response_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_query_runs_org_idempotency_unique": { + "name": "integration_query_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_query_runs_org_status_idx": { + "name": "integration_query_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_query_runs_organisation_id_organisations_id_fk": { + "name": "integration_query_runs_organisation_id_organisations_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_integration_id_integration_records_id_fk": { + "name": "integration_query_runs_integration_id_integration_records_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_template_id_integration_query_templates_id_fk": { + "name": "integration_query_runs_template_id_integration_query_templates_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "integration_query_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_requested_by_actor_id_actors_id_fk": { + "name": "integration_query_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_query_templates": { + "name": "integration_query_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_key": { + "name": "template_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "definition": { + "name": "definition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_templates_org_key_version_unique": { + "name": "integration_templates_org_key_version_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_query_templates_organisation_id_organisations_id_fk": { + "name": "integration_query_templates_organisation_id_organisations_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_templates_integration_id_integration_records_id_fk": { + "name": "integration_query_templates_integration_id_integration_records_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_templates_created_by_actor_id_actors_id_fk": { + "name": "integration_query_templates_created_by_actor_id_actors_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_records": { + "name": "integration_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mock": { + "name": "mock", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "configuration": { + "name": "configuration", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "health": { + "name": "health", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "cursor": { + "name": "cursor", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integrations_org_product_instance_unique": { + "name": "integrations_org_product_instance_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_records_organisation_id_organisations_id_fk": { + "name": "integration_records_organisation_id_organisations_id_fk", + "tableFrom": "integration_records", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.investigations": { + "name": "investigations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_number": { + "name": "investigation_number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "investigation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "lead_actor_id": { + "name": "lead_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_activity_at": { + "name": "last_activity_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "closed_at": { + "name": "closed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "recommendation": { + "name": "recommendation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disposition": { + "name": "disposition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "promotion_decision": { + "name": "promotion_decision", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "linked_kelpie_case_id": { + "name": "linked_kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "investigations_org_number_unique": { + "name": "investigations_org_number_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investigations_org_queue_idx": { + "name": "investigations_org_queue_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "severity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_activity_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investigations_search_idx": { + "name": "investigations_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"summary\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "investigations_organisation_id_organisations_id_fk": { + "name": "investigations_organisation_id_organisations_id_fk", + "tableFrom": "investigations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "investigations_lead_actor_id_actors_id_fk": { + "name": "investigations_lead_actor_id_actors_id_fk", + "tableFrom": "investigations", + "tableTo": "actors", + "columnsFrom": [ + "lead_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "investigations_room_id_rooms_id_fk": { + "name": "investigations_room_id_rooms_id_fk", + "tableFrom": "investigations", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message_mentions": { + "name": "message_mentions", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mentioned_actor_id": { + "name": "mentioned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "mention_type": { + "name": "mention_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mention_key": { + "name": "mention_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_mentions_org_actor_idx": { + "name": "message_mentions_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mentioned_actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_mentions_organisation_id_organisations_id_fk": { + "name": "message_mentions_organisation_id_organisations_id_fk", + "tableFrom": "message_mentions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_mentions_message_id_messages_id_fk": { + "name": "message_mentions_message_id_messages_id_fk", + "tableFrom": "message_mentions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_mentions_mentioned_actor_id_actors_id_fk": { + "name": "message_mentions_mentioned_actor_id_actors_id_fk", + "tableFrom": "message_mentions", + "tableTo": "actors", + "columnsFrom": [ + "mentioned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_mentions_message_id_mention_type_mention_key_pk": { + "name": "message_mentions_message_id_mention_type_mention_key_pk", + "columns": [ + "message_id", + "mention_type", + "mention_key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "message_mention_type_check": { + "name": "message_mention_type_check", + "value": "\"message_mentions\".\"mention_type\" in ('actor','room','everyone')" + } + }, + "isRLSEnabled": false + }, + "public.message_pins": { + "name": "message_pins", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pinned_by_actor_id": { + "name": "pinned_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_pins_org_room_idx": { + "name": "message_pins_org_room_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_pins_organisation_id_organisations_id_fk": { + "name": "message_pins_organisation_id_organisations_id_fk", + "tableFrom": "message_pins", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_room_id_rooms_id_fk": { + "name": "message_pins_room_id_rooms_id_fk", + "tableFrom": "message_pins", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_message_id_messages_id_fk": { + "name": "message_pins_message_id_messages_id_fk", + "tableFrom": "message_pins", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_pinned_by_actor_id_actors_id_fk": { + "name": "message_pins_pinned_by_actor_id_actors_id_fk", + "tableFrom": "message_pins", + "tableTo": "actors", + "columnsFrom": [ + "pinned_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_pins_room_id_message_id_pk": { + "name": "message_pins_room_id_message_id_pk", + "columns": [ + "room_id", + "message_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message_revisions": { + "name": "message_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_type": { + "name": "revision_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previous_document": { + "name": "previous_document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "previous_plain_text": { + "name": "previous_plain_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "next_document": { + "name": "next_document", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "next_plain_text": { + "name": "next_plain_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_revisions_org_message_idx": { + "name": "message_revisions_org_message_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "message_revisions_org_idempotency_unique": { + "name": "message_revisions_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_revisions_organisation_id_organisations_id_fk": { + "name": "message_revisions_organisation_id_organisations_id_fk", + "tableFrom": "message_revisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_revisions_message_id_messages_id_fk": { + "name": "message_revisions_message_id_messages_id_fk", + "tableFrom": "message_revisions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_revisions_actor_id_actors_id_fk": { + "name": "message_revisions_actor_id_actors_id_fk", + "tableFrom": "message_revisions", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "message_revision_type_check": { + "name": "message_revision_type_check", + "value": "\"message_revisions\".\"revision_type\" in ('edit','delete')" + } + }, + "isRLSEnabled": false + }, + "public.message_saves": { + "name": "message_saves", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_saves_org_actor_idx": { + "name": "message_saves_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_saves_organisation_id_organisations_id_fk": { + "name": "message_saves_organisation_id_organisations_id_fk", + "tableFrom": "message_saves", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_saves_message_id_messages_id_fk": { + "name": "message_saves_message_id_messages_id_fk", + "tableFrom": "message_saves", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_saves_actor_id_actors_id_fk": { + "name": "message_saves_actor_id_actors_id_fk", + "tableFrom": "message_saves", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_saves_message_id_actor_id_pk": { + "name": "message_saves_message_id_actor_id_pk", + "columns": [ + "message_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "thread_parent_id": { + "name": "thread_parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_actor_id": { + "name": "author_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_type": { + "name": "message_type", + "type": "message_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "document": { + "name": "document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "plain_text": { + "name": "plain_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "edited_at": { + "name": "edited_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "data_classification": { + "name": "data_classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "related_alert_id": { + "name": "related_alert_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_agent_run_id": { + "name": "related_agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_workflow_run_id": { + "name": "related_workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "messages_org_room_time_idx": { + "name": "messages_org_room_time_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_thread_idx": { + "name": "messages_thread_idx", + "columns": [ + { + "expression": "thread_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_org_idempotency_unique": { + "name": "messages_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"messages\".\"idempotency_key\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_search_idx": { + "name": "messages_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"plain_text\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "messages_organisation_id_organisations_id_fk": { + "name": "messages_organisation_id_organisations_id_fk", + "tableFrom": "messages", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_room_id_rooms_id_fk": { + "name": "messages_room_id_rooms_id_fk", + "tableFrom": "messages", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_author_actor_id_actors_id_fk": { + "name": "messages_author_actor_id_actors_id_fk", + "tableFrom": "messages", + "tableTo": "actors", + "columnsFrom": [ + "author_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_related_alert_id_alerts_id_fk": { + "name": "messages_related_alert_id_alerts_id_fk", + "tableFrom": "messages", + "tableTo": "alerts", + "columnsFrom": [ + "related_alert_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_related_investigation_id_investigations_id_fk": { + "name": "messages_related_investigation_id_investigations_id_fk", + "tableFrom": "messages", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "safe_preview": { + "name": "safe_preview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target": { + "name": "target", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_org_actor_read_idx": { + "name": "notifications_org_actor_read_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "read_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_organisation_id_organisations_id_fk": { + "name": "notifications_organisation_id_organisations_id_fk", + "tableFrom": "notifications", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "notifications_actor_id_actors_id_fk": { + "name": "notifications_actor_id_actors_id_fk", + "tableFrom": "notifications", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organisations": { + "name": "organisations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data_region": { + "name": "data_region", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'australia'" + }, + "default_timezone": { + "name": "default_timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Australia/Sydney'" + }, + "retention_policy": { + "name": "retention_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "authentication_policy": { + "name": "authentication_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organisations_slug_unique": { + "name": "organisations_slug_unique", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organisations_status_check": { + "name": "organisations_status_check", + "value": "\"organisations\".\"status\" in ('active','suspended')" + } + }, + "isRLSEnabled": false + }, + "public.outbox_events": { + "name": "outbox_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aggregate_type": { + "name": "aggregate_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aggregate_id": { + "name": "aggregate_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "queue_name": { + "name": "queue_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "dispatched_at": { + "name": "dispatched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "outbox_idempotency_unique": { + "name": "outbox_idempotency_unique", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_pending_idx": { + "name": "outbox_pending_idx", + "columns": [ + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"outbox_events\".\"dispatched_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "outbox_events_organisation_id_organisations_id_fk": { + "name": "outbox_events_organisation_id_organisations_id_fk", + "tableFrom": "outbox_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reaction_operations": { + "name": "reaction_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "emoji": { + "name": "emoji", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "result_count": { + "name": "result_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_operations_org_idempotency_unique": { + "name": "reaction_operations_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_operations_org_message_idx": { + "name": "reaction_operations_org_message_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_operations_organisation_id_organisations_id_fk": { + "name": "reaction_operations_organisation_id_organisations_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_operations_message_id_messages_id_fk": { + "name": "reaction_operations_message_id_messages_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_operations_actor_id_actors_id_fk": { + "name": "reaction_operations_actor_id_actors_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reaction_pack_assets": { + "name": "reaction_pack_assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_id": { + "name": "revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alt_text": { + "name": "alt_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "byte_size": { + "name": "byte_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "frame_count": { + "name": "frame_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verification_state": { + "name": "verification_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'verified'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_pack_assets_org_revision_name_unique": { + "name": "reaction_pack_assets_org_revision_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_pack_assets_org_digest_idx": { + "name": "reaction_pack_assets_org_digest_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_pack_assets_organisation_id_organisations_id_fk": { + "name": "reaction_pack_assets_organisation_id_organisations_id_fk", + "tableFrom": "reaction_pack_assets", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_assets_revision_id_reaction_pack_revisions_id_fk": { + "name": "reaction_pack_assets_revision_id_reaction_pack_revisions_id_fk", + "tableFrom": "reaction_pack_assets", + "tableTo": "reaction_pack_revisions", + "columnsFrom": [ + "revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_pack_assets_verification_check": { + "name": "reaction_pack_assets_verification_check", + "value": "\"reaction_pack_assets\".\"verification_state\" in ('verified','missing','mismatch')" + }, + "reaction_pack_assets_dimensions_check": { + "name": "reaction_pack_assets_dimensions_check", + "value": "\"reaction_pack_assets\".\"width\" > 0 and \"reaction_pack_assets\".\"height\" > 0 and \"reaction_pack_assets\".\"frame_count\" > 0 and \"reaction_pack_assets\".\"byte_size\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.reaction_pack_revisions": { + "name": "reaction_pack_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pack_id": { + "name": "pack_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_by_actor_id": { + "name": "approved_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_pack_revisions_org_pack_revision_unique": { + "name": "reaction_pack_revisions_org_pack_revision_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pack_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_pack_revisions_org_status_idx": { + "name": "reaction_pack_revisions_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_pack_revisions_organisation_id_organisations_id_fk": { + "name": "reaction_pack_revisions_organisation_id_organisations_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_pack_id_reaction_packs_id_fk": { + "name": "reaction_pack_revisions_pack_id_reaction_packs_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "reaction_packs", + "columnsFrom": [ + "pack_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_approved_by_actor_id_actors_id_fk": { + "name": "reaction_pack_revisions_approved_by_actor_id_actors_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "actors", + "columnsFrom": [ + "approved_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_created_by_actor_id_actors_id_fk": { + "name": "reaction_pack_revisions_created_by_actor_id_actors_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_pack_revisions_status_check": { + "name": "reaction_pack_revisions_status_check", + "value": "\"reaction_pack_revisions\".\"status\" in ('draft','approved','superseded','removed')" + }, + "reaction_pack_revisions_revision_check": { + "name": "reaction_pack_revisions_revision_check", + "value": "\"reaction_pack_revisions\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.reaction_packs": { + "name": "reaction_packs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle": { + "name": "lifecycle", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "removed_by_actor_id": { + "name": "removed_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_packs_org_slug_unique": { + "name": "reaction_packs_org_slug_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_packs_org_lifecycle_idx": { + "name": "reaction_packs_org_lifecycle_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lifecycle", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_packs_organisation_id_organisations_id_fk": { + "name": "reaction_packs_organisation_id_organisations_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_packs_created_by_actor_id_actors_id_fk": { + "name": "reaction_packs_created_by_actor_id_actors_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_packs_removed_by_actor_id_actors_id_fk": { + "name": "reaction_packs_removed_by_actor_id_actors_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "actors", + "columnsFrom": [ + "removed_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_packs_lifecycle_check": { + "name": "reaction_packs_lifecycle_check", + "value": "\"reaction_packs\".\"lifecycle\" in ('active','removed')" + } + }, + "isRLSEnabled": false + }, + "public.reactions": { + "name": "reactions", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "emoji": { + "name": "emoji", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "reactions_organisation_id_organisations_id_fk": { + "name": "reactions_organisation_id_organisations_id_fk", + "tableFrom": "reactions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reactions_message_id_messages_id_fk": { + "name": "reactions_message_id_messages_id_fk", + "tableFrom": "reactions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reactions_actor_id_actors_id_fk": { + "name": "reactions_actor_id_actors_id_fk", + "tableFrom": "reactions", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "reactions_message_id_actor_id_emoji_pk": { + "name": "reactions_message_id_actor_id_emoji_pk", + "columns": [ + "message_id", + "actor_id", + "emoji" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.report_deliveries": { + "name": "report_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "report_id": { + "name": "report_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recipient": { + "name": "recipient", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'awaiting_approval'" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_deliveries_org_idempotency_unique": { + "name": "report_deliveries_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_deliveries_org_report_status_idx": { + "name": "report_deliveries_org_report_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "report_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_deliveries_organisation_id_organisations_id_fk": { + "name": "report_deliveries_organisation_id_organisations_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_report_id_report_manifests_id_fk": { + "name": "report_deliveries_report_id_report_manifests_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "report_manifests", + "columnsFrom": [ + "report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_approval_id_approvals_id_fk": { + "name": "report_deliveries_approval_id_approvals_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_requested_by_actor_id_actors_id_fk": { + "name": "report_deliveries_requested_by_actor_id_actors_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_deliveries_status_check": { + "name": "report_deliveries_status_check", + "value": "\"report_deliveries\".\"status\" in ('awaiting_approval','queued','delivered','failed','cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.report_manifests": { + "name": "report_manifests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "review_note": { + "name": "review_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "posted_message_id": { + "name": "posted_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_manifests_org_idempotency_unique": { + "name": "report_manifests_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_manifests_org_room_status_idx": { + "name": "report_manifests_org_room_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_manifests_organisation_id_organisations_id_fk": { + "name": "report_manifests_organisation_id_organisations_id_fk", + "tableFrom": "report_manifests", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_agent_run_id_agent_runs_id_fk": { + "name": "report_manifests_agent_run_id_agent_runs_id_fk", + "tableFrom": "report_manifests", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_task_id_tasks_id_fk": { + "name": "report_manifests_task_id_tasks_id_fk", + "tableFrom": "report_manifests", + "tableTo": "tasks", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_room_id_rooms_id_fk": { + "name": "report_manifests_room_id_rooms_id_fk", + "tableFrom": "report_manifests", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_requested_by_actor_id_actors_id_fk": { + "name": "report_manifests_requested_by_actor_id_actors_id_fk", + "tableFrom": "report_manifests", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_posted_message_id_messages_id_fk": { + "name": "report_manifests_posted_message_id_messages_id_fk", + "tableFrom": "report_manifests", + "tableTo": "messages", + "columnsFrom": [ + "posted_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_manifests_status_check": { + "name": "report_manifests_status_check", + "value": "\"report_manifests\".\"status\" in ('draft','reviewed','posted','superseded')" + }, + "report_manifests_version_check": { + "name": "report_manifests_version_check", + "value": "\"report_manifests\".\"version\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.report_schedules": { + "name": "report_schedules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cadence": { + "name": "cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "audience": { + "name": "audience", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'leadership'" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_schedules_org_idempotency_unique": { + "name": "report_schedules_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_schedules_org_due_idx": { + "name": "report_schedules_org_due_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_schedules_organisation_id_organisations_id_fk": { + "name": "report_schedules_organisation_id_organisations_id_fk", + "tableFrom": "report_schedules", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_schedules_room_id_rooms_id_fk": { + "name": "report_schedules_room_id_rooms_id_fk", + "tableFrom": "report_schedules", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_schedules_created_by_actor_id_actors_id_fk": { + "name": "report_schedules_created_by_actor_id_actors_id_fk", + "tableFrom": "report_schedules", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_schedules_cadence_check": { + "name": "report_schedules_cadence_check", + "value": "\"report_schedules\".\"cadence\" in ('weekly','monthly')" + }, + "report_schedules_audience_check": { + "name": "report_schedules_audience_check", + "value": "\"report_schedules\".\"audience\" in ('analyst','leadership','executive')" + } + }, + "isRLSEnabled": false + }, + "public.research_items": { + "name": "research_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watchlist_id": { + "name": "watchlist_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "research_run_id": { + "name": "research_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_published_at": { + "name": "source_published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "root_message_id": { + "name": "root_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "latest_message_id": { + "name": "latest_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "brief": { + "name": "brief", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feedback_by_actor_id": { + "name": "feedback_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "feedback_at": { + "name": "feedback_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_items_org_fingerprint_unique": { + "name": "research_items_org_fingerprint_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_items_org_watchlist_idx": { + "name": "research_items_org_watchlist_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "watchlist_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_items_organisation_id_organisations_id_fk": { + "name": "research_items_organisation_id_organisations_id_fk", + "tableFrom": "research_items", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_watchlist_id_research_watchlists_id_fk": { + "name": "research_items_watchlist_id_research_watchlists_id_fk", + "tableFrom": "research_items", + "tableTo": "research_watchlists", + "columnsFrom": [ + "watchlist_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_research_run_id_research_runs_id_fk": { + "name": "research_items_research_run_id_research_runs_id_fk", + "tableFrom": "research_items", + "tableTo": "research_runs", + "columnsFrom": [ + "research_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_root_message_id_messages_id_fk": { + "name": "research_items_root_message_id_messages_id_fk", + "tableFrom": "research_items", + "tableTo": "messages", + "columnsFrom": [ + "root_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_latest_message_id_messages_id_fk": { + "name": "research_items_latest_message_id_messages_id_fk", + "tableFrom": "research_items", + "tableTo": "messages", + "columnsFrom": [ + "latest_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_feedback_by_actor_id_actors_id_fk": { + "name": "research_items_feedback_by_actor_id_actors_id_fk", + "tableFrom": "research_items", + "tableTo": "actors", + "columnsFrom": [ + "feedback_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.research_runs": { + "name": "research_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watchlist_id": { + "name": "watchlist_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "source_limit": { + "name": "source_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_budget": { + "name": "token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cost_limit_cents": { + "name": "cost_limit_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "time_limit_seconds": { + "name": "time_limit_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_runs_org_idempotency_unique": { + "name": "research_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_runs_org_agent_unique": { + "name": "research_runs_org_agent_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_runs_org_status_idx": { + "name": "research_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_runs_organisation_id_organisations_id_fk": { + "name": "research_runs_organisation_id_organisations_id_fk", + "tableFrom": "research_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_runs_watchlist_id_research_watchlists_id_fk": { + "name": "research_runs_watchlist_id_research_watchlists_id_fk", + "tableFrom": "research_runs", + "tableTo": "research_watchlists", + "columnsFrom": [ + "watchlist_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_runs_agent_run_id_agent_runs_id_fk": { + "name": "research_runs_agent_run_id_agent_runs_id_fk", + "tableFrom": "research_runs", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "research_runs_status_check": { + "name": "research_runs_status_check", + "value": "\"research_runs\".\"status\" in ('queued','running','completed','failed')" + } + }, + "isRLSEnabled": false + }, + "public.research_watchlists": { + "name": "research_watchlists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vendors": { + "name": "vendors", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "technologies": { + "name": "technologies", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "sources": { + "name": "sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "cadence_minutes": { + "name": "cadence_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 240 + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_watchlists_org_name_unique": { + "name": "research_watchlists_org_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_watchlists_due_idx": { + "name": "research_watchlists_due_idx", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_watchlists_organisation_id_organisations_id_fk": { + "name": "research_watchlists_organisation_id_organisations_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_watchlists_room_id_rooms_id_fk": { + "name": "research_watchlists_room_id_rooms_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_watchlists_created_by_actor_id_actors_id_fk": { + "name": "research_watchlists_created_by_actor_id_actors_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "research_watchlists_cadence_check": { + "name": "research_watchlists_cadence_check", + "value": "\"research_watchlists\".\"cadence_minutes\" between 15 and 10080" + } + }, + "isRLSEnabled": false + }, + "public.room_integration_bindings": { + "name": "room_integration_bindings", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "room_integration_bindings_org_room_idx": { + "name": "room_integration_bindings_org_room_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_integration_bindings_organisation_id_organisations_id_fk": { + "name": "room_integration_bindings_organisation_id_organisations_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_room_id_rooms_id_fk": { + "name": "room_integration_bindings_room_id_rooms_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_integration_id_integration_records_id_fk": { + "name": "room_integration_bindings_integration_id_integration_records_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_created_by_actor_id_actors_id_fk": { + "name": "room_integration_bindings_created_by_actor_id_actors_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "room_integration_bindings_room_id_integration_id_pk": { + "name": "room_integration_bindings_room_id_integration_id_pk", + "columns": [ + "room_id", + "integration_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.room_invitations": { + "name": "room_invitations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invited_actor_id": { + "name": "invited_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_expires_at": { + "name": "access_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invited_by_actor_id": { + "name": "invited_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "responded_at": { + "name": "responded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "room_invitations_org_idempotency_unique": { + "name": "room_invitations_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "room_invitations_org_room_status_idx": { + "name": "room_invitations_org_room_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_invitations_organisation_id_organisations_id_fk": { + "name": "room_invitations_organisation_id_organisations_id_fk", + "tableFrom": "room_invitations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_room_id_rooms_id_fk": { + "name": "room_invitations_room_id_rooms_id_fk", + "tableFrom": "room_invitations", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_invited_actor_id_actors_id_fk": { + "name": "room_invitations_invited_actor_id_actors_id_fk", + "tableFrom": "room_invitations", + "tableTo": "actors", + "columnsFrom": [ + "invited_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_invited_by_actor_id_actors_id_fk": { + "name": "room_invitations_invited_by_actor_id_actors_id_fk", + "tableFrom": "room_invitations", + "tableTo": "actors", + "columnsFrom": [ + "invited_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "room_invitation_role_check": { + "name": "room_invitation_role_check", + "value": "\"room_invitations\".\"membership_role\" in ('moderator','member','guest','agent_member')" + }, + "room_invitation_status_check": { + "name": "room_invitation_status_check", + "value": "\"room_invitations\".\"status\" in ('pending','accepted','revoked','expired')" + } + }, + "isRLSEnabled": false + }, + "public.room_memberships": { + "name": "room_memberships", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notification_level": { + "name": "notification_level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "notify_replies": { + "name": "notify_replies", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "notify_followed_threads": { + "name": "notify_followed_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_read_event_id": { + "name": "last_read_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "muted": { + "name": "muted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "favourite": { + "name": "favourite", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sidebar_position": { + "name": "sidebar_position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sidebar_group": { + "name": "sidebar_group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_expires_at": { + "name": "access_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "room_memberships_org_actor_idx": { + "name": "room_memberships_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_memberships_organisation_id_organisations_id_fk": { + "name": "room_memberships_organisation_id_organisations_id_fk", + "tableFrom": "room_memberships", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_memberships_room_id_rooms_id_fk": { + "name": "room_memberships_room_id_rooms_id_fk", + "tableFrom": "room_memberships", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_memberships_actor_id_actors_id_fk": { + "name": "room_memberships_actor_id_actors_id_fk", + "tableFrom": "room_memberships", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "room_memberships_room_id_actor_id_pk": { + "name": "room_memberships_room_id_actor_id_pk", + "columns": [ + "room_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "room_membership_role_check": { + "name": "room_membership_role_check", + "value": "\"room_memberships\".\"membership_role\" in ('owner','moderator','member','guest','agent_member')" + } + }, + "isRLSEnabled": false + }, + "public.rooms": { + "name": "rooms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "room_type": { + "name": "room_type", + "type": "room_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'organisation'" + }, + "topic": { + "name": "topic", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "policies": { + "name": "policies", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "direct_fingerprint": { + "name": "direct_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "linked_investigation_id": { + "name": "linked_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "linked_kelpie_case_id": { + "name": "linked_kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_severity": { + "name": "default_severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "tlp": { + "name": "tlp", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'amber'" + }, + "retention_policy": { + "name": "retention_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "rooms_org_slug_unique": { + "name": "rooms_org_slug_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rooms_org_direct_fingerprint_unique": { + "name": "rooms_org_direct_fingerprint_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "direct_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"rooms\".\"direct_fingerprint\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "rooms_org_type_idx": { + "name": "rooms_org_type_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "rooms_organisation_id_organisations_id_fk": { + "name": "rooms_organisation_id_organisations_id_fk", + "tableFrom": "rooms", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "rooms_created_by_actor_id_actors_id_fk": { + "name": "rooms_created_by_actor_id_actors_id_fk", + "tableFrom": "rooms", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tasks": { + "name": "tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "task_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'backlog'" + }, + "priority": { + "name": "priority", + "type": "task_priority", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "assigned_actor_id": { + "name": "assigned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approval_required": { + "name": "approval_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "due_at": { + "name": "due_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_run_status": { + "name": "agent_run_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tasks_org_status_idx": { + "name": "tasks_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tasks_org_assignee_idx": { + "name": "tasks_org_assignee_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "assigned_actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tasks_org_idempotency_unique": { + "name": "tasks_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tasks_organisation_id_organisations_id_fk": { + "name": "tasks_organisation_id_organisations_id_fk", + "tableFrom": "tasks", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_assigned_actor_id_actors_id_fk": { + "name": "tasks_assigned_actor_id_actors_id_fk", + "tableFrom": "tasks", + "tableTo": "actors", + "columnsFrom": [ + "assigned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_created_by_actor_id_actors_id_fk": { + "name": "tasks_created_by_actor_id_actors_id_fk", + "tableFrom": "tasks", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_room_id_rooms_id_fk": { + "name": "tasks_room_id_rooms_id_fk", + "tableFrom": "tasks", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_investigation_id_investigations_id_fk": { + "name": "tasks_investigation_id_investigations_id_fk", + "tableFrom": "tasks", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_follows": { + "name": "thread_follows", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "root_message_id": { + "name": "root_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "thread_follows_org_actor_idx": { + "name": "thread_follows_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "thread_follows_organisation_id_organisations_id_fk": { + "name": "thread_follows_organisation_id_organisations_id_fk", + "tableFrom": "thread_follows", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "thread_follows_root_message_id_messages_id_fk": { + "name": "thread_follows_root_message_id_messages_id_fk", + "tableFrom": "thread_follows", + "tableTo": "messages", + "columnsFrom": [ + "root_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "thread_follows_actor_id_actors_id_fk": { + "name": "thread_follows_actor_id_actors_id_fk", + "tableFrom": "thread_follows", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "thread_follows_root_message_id_actor_id_pk": { + "name": "thread_follows_root_message_id_actor_id_pk", + "columns": [ + "root_message_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.timeline_events": { + "name": "timeline_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "external_case_id": { + "name": "external_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "timeline_org_investigation_time_idx": { + "name": "timeline_org_investigation_time_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "timeline_events_organisation_id_organisations_id_fk": { + "name": "timeline_events_organisation_id_organisations_id_fk", + "tableFrom": "timeline_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_investigation_id_investigations_id_fk": { + "name": "timeline_events_investigation_id_investigations_id_fk", + "tableFrom": "timeline_events", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_room_id_rooms_id_fk": { + "name": "timeline_events_room_id_rooms_id_fk", + "tableFrom": "timeline_events", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_actor_id_actors_id_fk": { + "name": "timeline_events_actor_id_actors_id_fk", + "tableFrom": "timeline_events", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "better_auth_user_id": { + "name": "better_auth_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "job_title": { + "name": "job_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "team": { + "name": "team", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "presence_state": { + "name": "presence_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'offline'" + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Australia/Sydney'" + }, + "notification_preferences": { + "name": "notification_preferences", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_active_at": { + "name": "last_active_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_org_email_unique": { + "name": "users_org_email_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_better_auth_unique": { + "name": "users_better_auth_unique", + "columns": [ + { + "expression": "better_auth_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_org_idx": { + "name": "users_org_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_organisation_id_organisations_id_fk": { + "name": "users_organisation_id_organisations_id_fk", + "tableFrom": "users", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "users_better_auth_user_id_auth_user_id_fk": { + "name": "users_better_auth_user_id_auth_user_id_fk", + "tableFrom": "users", + "tableTo": "auth_user", + "columnsFrom": [ + "better_auth_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_definitions": { + "name": "workflow_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_key": { + "name": "workflow_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "yaml": { + "name": "yaml", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parsed": { + "name": "parsed", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "owner_actor_id": { + "name": "owner_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_defs_org_key_version_unique": { + "name": "workflow_defs_org_key_version_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_definitions_organisation_id_organisations_id_fk": { + "name": "workflow_definitions_organisation_id_organisations_id_fk", + "tableFrom": "workflow_definitions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_definitions_owner_actor_id_actors_id_fk": { + "name": "workflow_definitions_owner_actor_id_actors_id_fk", + "tableFrom": "workflow_definitions", + "tableTo": "actors", + "columnsFrom": [ + "owner_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_runs": { + "name": "workflow_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_definition_id": { + "name": "workflow_definition_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "trigger_event_id": { + "name": "trigger_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "workflow_runs_org_idempotency_unique": { + "name": "workflow_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_runs_organisation_id_organisations_id_fk": { + "name": "workflow_runs_organisation_id_organisations_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_workflow_definition_id_workflow_definitions_id_fk": { + "name": "workflow_runs_workflow_definition_id_workflow_definitions_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflow_definitions", + "columnsFrom": [ + "workflow_definition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_room_id_rooms_id_fk": { + "name": "workflow_runs_room_id_rooms_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_investigation_id_investigations_id_fk": { + "name": "workflow_runs_investigation_id_investigations_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_requested_by_actor_id_actors_id_fk": { + "name": "workflow_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.actor_type": { + "name": "actor_type", + "schema": "public", + "values": [ + "human", + "agent", + "product", + "service", + "system" + ] + }, + "public.alert_status": { + "name": "alert_status", + "schema": "public", + "values": [ + "new", + "acknowledged", + "investigating", + "dismissed", + "promoted", + "closed" + ] + }, + "public.approval_status": { + "name": "approval_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected", + "expired", + "cancelled", + "executed", + "failed" + ] + }, + "public.investigation_status": { + "name": "investigation_status", + "schema": "public", + "values": [ + "open", + "triaging", + "investigating", + "awaiting_approval", + "promoted", + "closed" + ] + }, + "public.message_type": { + "name": "message_type", + "schema": "public", + "values": [ + "text", + "system", + "alert", + "finding", + "decision", + "approval", + "workflow", + "agent-status", + "query-result", + "evidence", + "case-event", + "response-action" + ] + }, + "public.room_type": { + "name": "room_type", + "schema": "public", + "values": [ + "operations", + "incident", + "investigation", + "hunt", + "engineering", + "private", + "direct", + "system" + ] + }, + "public.severity": { + "name": "severity", + "schema": "public", + "values": [ + "critical", + "high", + "medium", + "low", + "informational" + ] + }, + "public.task_priority": { + "name": "task_priority", + "schema": "public", + "values": [ + "urgent", + "high", + "normal", + "low" + ] + }, + "public.task_status": { + "name": "task_status", + "schema": "public", + "values": [ + "backlog", + "ready", + "in_progress", + "review", + "done" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/database/migrations/meta/0018_snapshot.json b/packages/database/migrations/meta/0018_snapshot.json new file mode 100644 index 0000000..5edcedc --- /dev/null +++ b/packages/database/migrations/meta/0018_snapshot.json @@ -0,0 +1,11363 @@ +{ + "id": "71b9ed46-7893-4ce6-ba96-f34a03d21ed0", + "prevId": "3f61ffb8-af0d-45ef-a0be-7f325899949f", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.actors": { + "name": "actors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "actor_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "identity_reference": { + "name": "identity_reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capability_assignments": { + "name": "capability_assignments", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "actors_org_type_idx": { + "name": "actors_org_type_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "actors_org_identity_unique": { + "name": "actors_org_identity_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "identity_reference", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"actors\".\"identity_reference\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "actors_organisation_id_organisations_id_fk": { + "name": "actors_organisation_id_organisations_id_fk", + "tableFrom": "actors", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_definitions": { + "name": "agent_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runtime": { + "name": "runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_actor_id": { + "name": "owner_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "system_prompt_version": { + "name": "system_prompt_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allowed_tools": { + "name": "allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "allowed_rooms": { + "name": "allowed_rooms", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "capability_requirements": { + "name": "capability_requirements", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "maximum_runtime_seconds": { + "name": "maximum_runtime_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 300 + }, + "maximum_token_budget": { + "name": "maximum_token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20000 + }, + "maximum_cost_cents": { + "name": "maximum_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 500 + }, + "data_classification_allowance": { + "name": "data_classification_allowance", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"internal\"]'::jsonb" + }, + "approval_requirements": { + "name": "approval_requirements", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "requested_permission_mode": { + "name": "requested_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'read_only'" + }, + "kill_switch": { + "name": "kill_switch", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_definitions_org_name_unique": { + "name": "agent_definitions_org_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_definitions_organisation_id_organisations_id_fk": { + "name": "agent_definitions_organisation_id_organisations_id_fk", + "tableFrom": "agent_definitions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_definitions_owner_actor_id_actors_id_fk": { + "name": "agent_definitions_owner_actor_id_actors_id_fk", + "tableFrom": "agent_definitions", + "tableTo": "actors", + "columnsFrom": [ + "owner_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_definitions_requested_permission_check": { + "name": "agent_definitions_requested_permission_check", + "value": "\"agent_definitions\".\"requested_permission_mode\" in ('read_only','approval_gated')" + } + }, + "isRLSEnabled": false + }, + "public.agent_memories": { + "name": "agent_memories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "supersedes_memory_id": { + "name": "supersedes_memory_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reviewed_by_actor_id": { + "name": "reviewed_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_memories_org_agent_idx": { + "name": "agent_memories_org_agent_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_memories_organisation_id_organisations_id_fk": { + "name": "agent_memories_organisation_id_organisations_id_fk", + "tableFrom": "agent_memories", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_agent_id_agent_definitions_id_fk": { + "name": "agent_memories_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_memories", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_source_run_id_agent_runs_id_fk": { + "name": "agent_memories_source_run_id_agent_runs_id_fk", + "tableFrom": "agent_memories", + "tableTo": "agent_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_reviewed_by_actor_id_actors_id_fk": { + "name": "agent_memories_reviewed_by_actor_id_actors_id_fk", + "tableFrom": "agent_memories", + "tableTo": "actors", + "columnsFrom": [ + "reviewed_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_memories_kind_check": { + "name": "agent_memories_kind_check", + "value": "\"agent_memories\".\"kind\" in ('fact','preference','lesson','failure','procedure_hint')" + }, + "agent_memories_status_check": { + "name": "agent_memories_status_check", + "value": "\"agent_memories\".\"status\" in ('active','superseded','expired','rejected')" + }, + "agent_memories_confidence_check": { + "name": "agent_memories_confidence_check", + "value": "\"agent_memories\".\"confidence\" between 0 and 100" + } + }, + "isRLSEnabled": false + }, + "public.agent_readiness_snapshots": { + "name": "agent_readiness_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "process_identity": { + "name": "process_identity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "gateway_state": { + "name": "gateway_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authentication_state": { + "name": "authentication_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "observer_state": { + "name": "observer_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle_evidence_state": { + "name": "lifecycle_evidence_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle_state": { + "name": "lifecycle_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "capability_state": { + "name": "capability_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_state": { + "name": "tool_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_state": { + "name": "permission_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_runtime": { + "name": "reported_runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_provider": { + "name": "reported_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_model": { + "name": "reported_model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_capabilities": { + "name": "input_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "output_capabilities": { + "name": "output_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "available_commands": { + "name": "available_commands", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "tool_sources": { + "name": "tool_sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "tool_risk_classes": { + "name": "tool_risk_classes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "requested_permission_mode": { + "name": "requested_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_permission_mode": { + "name": "effective_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "limitations": { + "name": "limitations", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_readiness_org_agent_verified_idx": { + "name": "agent_readiness_org_agent_verified_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "verified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_readiness_org_process_verified_idx": { + "name": "agent_readiness_org_process_verified_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "process_identity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "verified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_readiness_snapshots_organisation_id_organisations_id_fk": { + "name": "agent_readiness_snapshots_organisation_id_organisations_id_fk", + "tableFrom": "agent_readiness_snapshots", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_readiness_snapshots_agent_id_agent_definitions_id_fk": { + "name": "agent_readiness_snapshots_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_readiness_snapshots", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_readiness_evidence_states_check": { + "name": "agent_readiness_evidence_states_check", + "value": "\"agent_readiness_snapshots\".\"gateway_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"authentication_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"observer_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"lifecycle_evidence_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"capability_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"tool_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"permission_state\" in ('reported','unavailable','unknown')" + }, + "agent_readiness_lifecycle_state_check": { + "name": "agent_readiness_lifecycle_state_check", + "value": "\"agent_readiness_snapshots\".\"lifecycle_state\" in ('idle','running','stopped','failed','unknown')" + }, + "agent_readiness_permission_modes_check": { + "name": "agent_readiness_permission_modes_check", + "value": "\"agent_readiness_snapshots\".\"requested_permission_mode\" in ('read_only','approval_gated','unknown')\n and \"agent_readiness_snapshots\".\"effective_permission_mode\" in ('read_only','approval_gated','unknown')" + } + }, + "isRLSEnabled": false + }, + "public.agent_run_events": { + "name": "agent_run_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_run_events_org_run_idx": { + "name": "agent_run_events_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_events_organisation_id_organisations_id_fk": { + "name": "agent_run_events_organisation_id_organisations_id_fk", + "tableFrom": "agent_run_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_run_events_run_id_agent_runs_id_fk": { + "name": "agent_run_events_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_events", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_run_sources": { + "name": "agent_run_sources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_run_sources_org_run_unique": { + "name": "agent_run_sources_org_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_run_sources_org_run_idx": { + "name": "agent_run_sources_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_sources_organisation_id_organisations_id_fk": { + "name": "agent_run_sources_organisation_id_organisations_id_fk", + "tableFrom": "agent_run_sources", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_run_sources_run_id_agent_runs_id_fk": { + "name": "agent_run_sources_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_sources", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_runs": { + "name": "agent_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workflow_run_id": { + "name": "workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "request": { + "name": "request", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "progress": { + "name": "progress", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deadline_at": { + "name": "deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancellation_requested_at": { + "name": "cancellation_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "input_hash": { + "name": "input_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt_hash": { + "name": "prompt_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output_hash": { + "name": "output_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output_schema": { + "name": "output_schema", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "runtime": { + "name": "runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "maximum_runtime_seconds": { + "name": "maximum_runtime_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 300 + }, + "maximum_token_budget": { + "name": "maximum_token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20000 + }, + "maximum_cost_cents": { + "name": "maximum_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 500 + }, + "token_usage": { + "name": "token_usage", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "estimated_cost_cents": { + "name": "estimated_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tool_call_count": { + "name": "tool_call_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "diagnostics": { + "name": "diagnostics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cancellation_reason": { + "name": "cancellation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "structured_output": { + "name": "structured_output", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "agent_runs_org_idempotency_unique": { + "name": "agent_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_runs_org_status_idx": { + "name": "agent_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_runs_recovery_idx": { + "name": "agent_runs_recovery_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lease_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_runs_agent_id_agent_definitions_id_fk": { + "name": "agent_runs_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_runs", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_organisation_id_organisations_id_fk": { + "name": "agent_runs_organisation_id_organisations_id_fk", + "tableFrom": "agent_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_room_id_rooms_id_fk": { + "name": "agent_runs_room_id_rooms_id_fk", + "tableFrom": "agent_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_investigation_id_investigations_id_fk": { + "name": "agent_runs_investigation_id_investigations_id_fk", + "tableFrom": "agent_runs", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_requested_by_actor_id_actors_id_fk": { + "name": "agent_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "agent_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_skill_evaluations": { + "name": "agent_skill_evaluations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_version_id": { + "name": "skill_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "evaluator_actor_id": { + "name": "evaluator_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "suite": { + "name": "suite", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "passed": { + "name": "passed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "score": { + "name": "score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "baseline_score": { + "name": "baseline_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "regressions": { + "name": "regressions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skill_evaluations_version_idx": { + "name": "agent_skill_evaluations_version_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skill_evaluations_organisation_id_organisations_id_fk": { + "name": "agent_skill_evaluations_organisation_id_organisations_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_evaluations_skill_version_id_agent_skill_versions_id_fk": { + "name": "agent_skill_evaluations_skill_version_id_agent_skill_versions_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "agent_skill_versions", + "columnsFrom": [ + "skill_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_evaluations_evaluator_actor_id_actors_id_fk": { + "name": "agent_skill_evaluations_evaluator_actor_id_actors_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "actors", + "columnsFrom": [ + "evaluator_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skill_evaluations_score_check": { + "name": "agent_skill_evaluations_score_check", + "value": "\"agent_skill_evaluations\".\"score\" between 0 and 100" + } + }, + "isRLSEnabled": false + }, + "public.agent_skill_versions": { + "name": "agent_skill_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_id": { + "name": "skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "based_on_version_id": { + "name": "based_on_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "change_rationale": { + "name": "change_rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "required_capabilities": { + "name": "required_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "allowed_tools": { + "name": "allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'proposed'" + }, + "approved_by_actor_id": { + "name": "approved_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skill_versions_skill_version_unique": { + "name": "agent_skill_versions_skill_version_unique", + "columns": [ + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_skill_versions_content_hash_unique": { + "name": "agent_skill_versions_content_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "content_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skill_versions_organisation_id_organisations_id_fk": { + "name": "agent_skill_versions_organisation_id_organisations_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_skill_id_agent_skills_id_fk": { + "name": "agent_skill_versions_skill_id_agent_skills_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "agent_skills", + "columnsFrom": [ + "skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_source_run_id_agent_runs_id_fk": { + "name": "agent_skill_versions_source_run_id_agent_runs_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "agent_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_approved_by_actor_id_actors_id_fk": { + "name": "agent_skill_versions_approved_by_actor_id_actors_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "actors", + "columnsFrom": [ + "approved_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skill_versions_state_check": { + "name": "agent_skill_versions_state_check", + "value": "\"agent_skill_versions\".\"state\" in ('proposed','evaluating','approved','rejected','published','rolled_back')" + } + }, + "isRLSEnabled": false + }, + "public.agent_skills": { + "name": "agent_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_key": { + "name": "skill_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "active_version_id": { + "name": "active_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skills_org_agent_key_unique": { + "name": "agent_skills_org_agent_key_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skills_organisation_id_organisations_id_fk": { + "name": "agent_skills_organisation_id_organisations_id_fk", + "tableFrom": "agent_skills", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skills_agent_id_agent_definitions_id_fk": { + "name": "agent_skills_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_skills", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skills_created_by_actor_id_actors_id_fk": { + "name": "agent_skills_created_by_actor_id_actors_id_fk", + "tableFrom": "agent_skills", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skills_status_check": { + "name": "agent_skills_status_check", + "value": "\"agent_skills\".\"status\" in ('draft','evaluating','published','retired')" + } + }, + "isRLSEnabled": false + }, + "public.agent_tool_calls": { + "name": "agent_tool_calls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "capability": { + "name": "capability", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "arguments_hash": { + "name": "arguments_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "result_hash": { + "name": "result_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "agent_tool_calls_org_run_idx": { + "name": "agent_tool_calls_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_tool_calls_organisation_id_organisations_id_fk": { + "name": "agent_tool_calls_organisation_id_organisations_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_tool_calls_run_id_agent_runs_id_fk": { + "name": "agent_tool_calls_run_id_agent_runs_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_tool_calls_approval_id_approvals_id_fk": { + "name": "agent_tool_calls_approval_id_approvals_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.alerts": { + "name": "alerts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_product": { + "name": "source_product", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_instance": { + "name": "source_instance", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_reference": { + "name": "external_reference", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "alert_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'new'" + }, + "rule_name": { + "name": "rule_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rule_id": { + "name": "rule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "assigned_actor_id": { + "name": "assigned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "entities": { + "name": "entities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "observables": { + "name": "observables", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "raw_reference_metadata": { + "name": "raw_reference_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kelpie_case_id": { + "name": "kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "correlation_key": { + "name": "correlation_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "alerts_org_source_ref_unique": { + "name": "alerts_org_source_ref_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_product", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_instance", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_reference", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_org_dedupe_unique": { + "name": "alerts_org_dedupe_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_org_queue_idx": { + "name": "alerts_org_queue_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "severity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "received_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_search_idx": { + "name": "alerts_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"description\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "alerts_organisation_id_organisations_id_fk": { + "name": "alerts_organisation_id_organisations_id_fk", + "tableFrom": "alerts", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "alerts_assigned_actor_id_actors_id_fk": { + "name": "alerts_assigned_actor_id_actors_id_fk", + "tableFrom": "alerts", + "tableTo": "actors", + "columnsFrom": [ + "assigned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "alerts_room_id_rooms_id_fk": { + "name": "alerts_room_id_rooms_id_fk", + "tableFrom": "alerts", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.approvals": { + "name": "approvals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requesting_actor_id": { + "name": "requesting_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action_type": { + "name": "action_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "risk_summary": { + "name": "risk_summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "required_capability": { + "name": "required_capability", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required_approval_count": { + "name": "required_approval_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "approval_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "decisions": { + "name": "decisions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "decision_at": { + "name": "decision_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "approvals_org_idempotency_unique": { + "name": "approvals_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "approvals_org_status_idx": { + "name": "approvals_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "approvals_organisation_id_organisations_id_fk": { + "name": "approvals_organisation_id_organisations_id_fk", + "tableFrom": "approvals", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "approvals_requesting_actor_id_actors_id_fk": { + "name": "approvals_requesting_actor_id_actors_id_fk", + "tableFrom": "approvals", + "tableTo": "actors", + "columnsFrom": [ + "requesting_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_events": { + "name": "audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "actor_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previous_hash": { + "name": "previous_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_hash": { + "name": "event_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_org_sequence_unique": { + "name": "audit_org_sequence_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_org_hash_unique": { + "name": "audit_org_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_org_target_idx": { + "name": "audit_org_target_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_events_organisation_id_organisations_id_fk": { + "name": "audit_events_organisation_id_organisations_id_fk", + "tableFrom": "audit_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "audit_events_actor_id_actors_id_fk": { + "name": "audit_events_actor_id_actors_id_fk", + "tableFrom": "audit_events", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_account": { + "name": "auth_account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_accounts_user_idx": { + "name": "auth_accounts_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_accounts_provider_unique": { + "name": "auth_accounts_provider_unique", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_account_user_id_auth_user_id_fk": { + "name": "auth_account_user_id_auth_user_id_fk", + "tableFrom": "auth_account", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_passkey": { + "name": "auth_passkey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_type": { + "name": "device_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backed_up": { + "name": "backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "aaguid": { + "name": "aaguid", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_passkey_user_idx": { + "name": "auth_passkey_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_passkey_user_id_auth_user_id_fk": { + "name": "auth_passkey_user_id_auth_user_id_fk", + "tableFrom": "auth_passkey", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_passkey_credential_id_unique": { + "name": "auth_passkey_credential_id_unique", + "nullsNotDistinct": false, + "columns": [ + "credential_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_session": { + "name": "auth_session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_sessions_user_idx": { + "name": "auth_sessions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_session_user_id_auth_user_id_fk": { + "name": "auth_session_user_id_auth_user_id_fk", + "tableFrom": "auth_session", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_session_token_unique": { + "name": "auth_session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_two_factor": { + "name": "auth_two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "failed_verification_count": { + "name": "failed_verification_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "locked_until": { + "name": "locked_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_two_factor_user_unique": { + "name": "auth_two_factor_user_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_two_factor_user_id_auth_user_id_fk": { + "name": "auth_two_factor_user_id_auth_user_id_fk", + "tableFrom": "auth_two_factor", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_user": { + "name": "auth_user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_user_email_unique": { + "name": "auth_user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_verification": { + "name": "auth_verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_verification_identifier_idx": { + "name": "auth_verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.decisions": { + "name": "decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "decision": { + "name": "decision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decision_maker_actor_id": { + "name": "decision_maker_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "alternatives_considered": { + "name": "alternatives_considered", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_workflow_run_id": { + "name": "related_workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decisions_org_investigation_idx": { + "name": "decisions_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "related_investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decisions_organisation_id_organisations_id_fk": { + "name": "decisions_organisation_id_organisations_id_fk", + "tableFrom": "decisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_decision_maker_actor_id_actors_id_fk": { + "name": "decisions_decision_maker_actor_id_actors_id_fk", + "tableFrom": "decisions", + "tableTo": "actors", + "columnsFrom": [ + "decision_maker_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_related_investigation_id_investigations_id_fk": { + "name": "decisions_related_investigation_id_investigations_id_fk", + "tableFrom": "decisions", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.evidence": { + "name": "evidence", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_by_actor_id": { + "name": "uploaded_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "related_room_id": { + "name": "related_room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_timestamp": { + "name": "original_timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scan_state": { + "name": "scan_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "retention_state": { + "name": "retention_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "legal_hold": { + "name": "legal_hold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "object_lock_metadata": { + "name": "object_lock_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "evidence_org_hash_unique": { + "name": "evidence_org_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "evidence_search_idx": { + "name": "evidence_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"file_name\" || ' ' || \"source\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "evidence_organisation_id_organisations_id_fk": { + "name": "evidence_organisation_id_organisations_id_fk", + "tableFrom": "evidence", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_uploaded_by_actor_id_actors_id_fk": { + "name": "evidence_uploaded_by_actor_id_actors_id_fk", + "tableFrom": "evidence", + "tableTo": "actors", + "columnsFrom": [ + "uploaded_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_related_room_id_rooms_id_fk": { + "name": "evidence_related_room_id_rooms_id_fk", + "tableFrom": "evidence", + "tableTo": "rooms", + "columnsFrom": [ + "related_room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_related_investigation_id_investigations_id_fk": { + "name": "evidence_related_investigation_id_investigations_id_fk", + "tableFrom": "evidence", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.findings": { + "name": "findings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "supporting_evidence": { + "name": "supporting_evidence", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_entities": { + "name": "related_entities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_observables": { + "name": "related_observables", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "recommended_action": { + "name": "recommended_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_provenance": { + "name": "agent_provenance", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "human_reviewed_at": { + "name": "human_reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "findings_org_investigation_idx": { + "name": "findings_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "findings_search_idx": { + "name": "findings_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"summary\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "findings_organisation_id_organisations_id_fk": { + "name": "findings_organisation_id_organisations_id_fk", + "tableFrom": "findings", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "findings_investigation_id_investigations_id_fk": { + "name": "findings_investigation_id_investigations_id_fk", + "tableFrom": "findings", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "findings_created_by_actor_id_actors_id_fk": { + "name": "findings_created_by_actor_id_actors_id_fk", + "tableFrom": "findings", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hunt_queries": { + "name": "hunt_queries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "hunt_id": { + "name": "hunt_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "query_run_id": { + "name": "query_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_key": { + "name": "source_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "hunt_queries_org_query_run_unique": { + "name": "hunt_queries_org_query_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "query_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_queries_org_hunt_sequence_unique": { + "name": "hunt_queries_org_hunt_sequence_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hunt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_queries_org_hunt_idx": { + "name": "hunt_queries_org_hunt_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hunt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hunt_queries_organisation_id_organisations_id_fk": { + "name": "hunt_queries_organisation_id_organisations_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_hunt_id_hunt_runs_id_fk": { + "name": "hunt_queries_hunt_id_hunt_runs_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "hunt_runs", + "columnsFrom": [ + "hunt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_integration_id_integration_records_id_fk": { + "name": "hunt_queries_integration_id_integration_records_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_template_id_integration_query_templates_id_fk": { + "name": "hunt_queries_template_id_integration_query_templates_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_query_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_query_run_id_integration_query_runs_id_fk": { + "name": "hunt_queries_query_run_id_integration_query_runs_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_query_runs", + "columnsFrom": [ + "query_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hunt_runs": { + "name": "hunt_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_message_id": { + "name": "source_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "linked_case_id": { + "name": "linked_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "training_mode": { + "name": "training_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "plan": { + "name": "plan", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'planned'" + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "hunt_runs_org_idempotency_unique": { + "name": "hunt_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_runs_org_agent_run_unique": { + "name": "hunt_runs_org_agent_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_runs_org_status_idx": { + "name": "hunt_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hunt_runs_organisation_id_organisations_id_fk": { + "name": "hunt_runs_organisation_id_organisations_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_agent_run_id_agent_runs_id_fk": { + "name": "hunt_runs_agent_run_id_agent_runs_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_task_id_tasks_id_fk": { + "name": "hunt_runs_task_id_tasks_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "tasks", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_source_message_id_messages_id_fk": { + "name": "hunt_runs_source_message_id_messages_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "messages", + "columnsFrom": [ + "source_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_room_id_rooms_id_fk": { + "name": "hunt_runs_room_id_rooms_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_requested_by_actor_id_actors_id_fk": { + "name": "hunt_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_approval_id_approvals_id_fk": { + "name": "hunt_runs_approval_id_approvals_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "hunt_runs_status_check": { + "name": "hunt_runs_status_check", + "value": "\"hunt_runs\".\"status\" in ('planned','awaiting_approval','querying','analysing','completed','failed','cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.hypotheses": { + "name": "hypotheses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "statement": { + "name": "statement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 50 + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "supporting_finding_ids": { + "name": "supporting_finding_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "contradicting_finding_ids": { + "name": "contradicting_finding_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "hypotheses_org_investigation_idx": { + "name": "hypotheses_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hypotheses_organisation_id_organisations_id_fk": { + "name": "hypotheses_organisation_id_organisations_id_fk", + "tableFrom": "hypotheses", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hypotheses_investigation_id_investigations_id_fk": { + "name": "hypotheses_investigation_id_investigations_id_fk", + "tableFrom": "hypotheses", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hypotheses_created_by_actor_id_actors_id_fk": { + "name": "hypotheses_created_by_actor_id_actors_id_fk", + "tableFrom": "hypotheses", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.idempotency_records": { + "name": "idempotency_records", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "response_status": { + "name": "response_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "response_body": { + "name": "response_body", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idempotency_expiry_idx": { + "name": "idempotency_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "idempotency_records_organisation_id_organisations_id_fk": { + "name": "idempotency_records_organisation_id_organisations_id_fk", + "tableFrom": "idempotency_records", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "idempotency_records_organisation_id_scope_key_pk": { + "name": "idempotency_records_organisation_id_scope_key_pk", + "columns": [ + "organisation_id", + "scope", + "key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_connector_credentials": { + "name": "integration_connector_credentials", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "encrypted_credential": { + "name": "encrypted_credential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "envelope_version": { + "name": "envelope_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'v1'" + }, + "rotation_version": { + "name": "rotation_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "rotated_by_actor_id": { + "name": "rotated_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_credentials_org_idx": { + "name": "integration_credentials_org_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_connector_credentials_organisation_id_organisations_id_fk": { + "name": "integration_connector_credentials_organisation_id_organisations_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_connector_credentials_integration_id_integration_records_id_fk": { + "name": "integration_connector_credentials_integration_id_integration_records_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_connector_credentials_rotated_by_actor_id_actors_id_fk": { + "name": "integration_connector_credentials_rotated_by_actor_id_actors_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "actors", + "columnsFrom": [ + "rotated_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_deliveries": { + "name": "integration_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "request_metadata": { + "name": "request_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "response_metadata": { + "name": "response_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_delivery_org_idempotency_unique": { + "name": "integration_delivery_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_deliveries_organisation_id_organisations_id_fk": { + "name": "integration_deliveries_organisation_id_organisations_id_fk", + "tableFrom": "integration_deliveries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_deliveries_integration_id_integration_records_id_fk": { + "name": "integration_deliveries_integration_id_integration_records_id_fk", + "tableFrom": "integration_deliveries", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_entities": { + "name": "integration_entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "posture": { + "name": "posture", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "integration_entities_org_external_unique": { + "name": "integration_entities_org_external_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_entities_organisation_id_organisations_id_fk": { + "name": "integration_entities_organisation_id_organisations_id_fk", + "tableFrom": "integration_entities", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_entities_integration_id_integration_records_id_fk": { + "name": "integration_entities_integration_id_integration_records_id_fk", + "tableFrom": "integration_entities", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_query_runs": { + "name": "integration_query_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "input": { + "name": "input", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "request_metadata": { + "name": "request_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "response_metadata": { + "name": "response_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_query_runs_org_idempotency_unique": { + "name": "integration_query_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_query_runs_org_status_idx": { + "name": "integration_query_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_query_runs_organisation_id_organisations_id_fk": { + "name": "integration_query_runs_organisation_id_organisations_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_integration_id_integration_records_id_fk": { + "name": "integration_query_runs_integration_id_integration_records_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_template_id_integration_query_templates_id_fk": { + "name": "integration_query_runs_template_id_integration_query_templates_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "integration_query_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_requested_by_actor_id_actors_id_fk": { + "name": "integration_query_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_query_templates": { + "name": "integration_query_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_key": { + "name": "template_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "definition": { + "name": "definition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_templates_org_key_version_unique": { + "name": "integration_templates_org_key_version_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_query_templates_organisation_id_organisations_id_fk": { + "name": "integration_query_templates_organisation_id_organisations_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_templates_integration_id_integration_records_id_fk": { + "name": "integration_query_templates_integration_id_integration_records_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_templates_created_by_actor_id_actors_id_fk": { + "name": "integration_query_templates_created_by_actor_id_actors_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_records": { + "name": "integration_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mock": { + "name": "mock", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "configuration": { + "name": "configuration", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "health": { + "name": "health", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "cursor": { + "name": "cursor", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integrations_org_product_instance_unique": { + "name": "integrations_org_product_instance_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_records_organisation_id_organisations_id_fk": { + "name": "integration_records_organisation_id_organisations_id_fk", + "tableFrom": "integration_records", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.investigations": { + "name": "investigations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_number": { + "name": "investigation_number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "investigation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "lead_actor_id": { + "name": "lead_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_activity_at": { + "name": "last_activity_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "closed_at": { + "name": "closed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "recommendation": { + "name": "recommendation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disposition": { + "name": "disposition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "promotion_decision": { + "name": "promotion_decision", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "linked_kelpie_case_id": { + "name": "linked_kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "investigations_org_number_unique": { + "name": "investigations_org_number_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investigations_org_queue_idx": { + "name": "investigations_org_queue_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "severity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_activity_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investigations_search_idx": { + "name": "investigations_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"summary\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "investigations_organisation_id_organisations_id_fk": { + "name": "investigations_organisation_id_organisations_id_fk", + "tableFrom": "investigations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "investigations_lead_actor_id_actors_id_fk": { + "name": "investigations_lead_actor_id_actors_id_fk", + "tableFrom": "investigations", + "tableTo": "actors", + "columnsFrom": [ + "lead_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "investigations_room_id_rooms_id_fk": { + "name": "investigations_room_id_rooms_id_fk", + "tableFrom": "investigations", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message_mentions": { + "name": "message_mentions", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mentioned_actor_id": { + "name": "mentioned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "mention_type": { + "name": "mention_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mention_key": { + "name": "mention_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_mentions_org_actor_idx": { + "name": "message_mentions_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mentioned_actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_mentions_organisation_id_organisations_id_fk": { + "name": "message_mentions_organisation_id_organisations_id_fk", + "tableFrom": "message_mentions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_mentions_message_id_messages_id_fk": { + "name": "message_mentions_message_id_messages_id_fk", + "tableFrom": "message_mentions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_mentions_mentioned_actor_id_actors_id_fk": { + "name": "message_mentions_mentioned_actor_id_actors_id_fk", + "tableFrom": "message_mentions", + "tableTo": "actors", + "columnsFrom": [ + "mentioned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_mentions_message_id_mention_type_mention_key_pk": { + "name": "message_mentions_message_id_mention_type_mention_key_pk", + "columns": [ + "message_id", + "mention_type", + "mention_key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "message_mention_type_check": { + "name": "message_mention_type_check", + "value": "\"message_mentions\".\"mention_type\" in ('actor','room','everyone')" + } + }, + "isRLSEnabled": false + }, + "public.message_pins": { + "name": "message_pins", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pinned_by_actor_id": { + "name": "pinned_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_pins_org_room_idx": { + "name": "message_pins_org_room_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_pins_organisation_id_organisations_id_fk": { + "name": "message_pins_organisation_id_organisations_id_fk", + "tableFrom": "message_pins", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_room_id_rooms_id_fk": { + "name": "message_pins_room_id_rooms_id_fk", + "tableFrom": "message_pins", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_message_id_messages_id_fk": { + "name": "message_pins_message_id_messages_id_fk", + "tableFrom": "message_pins", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_pinned_by_actor_id_actors_id_fk": { + "name": "message_pins_pinned_by_actor_id_actors_id_fk", + "tableFrom": "message_pins", + "tableTo": "actors", + "columnsFrom": [ + "pinned_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_pins_room_id_message_id_pk": { + "name": "message_pins_room_id_message_id_pk", + "columns": [ + "room_id", + "message_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message_revisions": { + "name": "message_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_type": { + "name": "revision_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previous_document": { + "name": "previous_document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "previous_plain_text": { + "name": "previous_plain_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "next_document": { + "name": "next_document", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "next_plain_text": { + "name": "next_plain_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_revisions_org_message_idx": { + "name": "message_revisions_org_message_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "message_revisions_org_idempotency_unique": { + "name": "message_revisions_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_revisions_organisation_id_organisations_id_fk": { + "name": "message_revisions_organisation_id_organisations_id_fk", + "tableFrom": "message_revisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_revisions_message_id_messages_id_fk": { + "name": "message_revisions_message_id_messages_id_fk", + "tableFrom": "message_revisions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_revisions_actor_id_actors_id_fk": { + "name": "message_revisions_actor_id_actors_id_fk", + "tableFrom": "message_revisions", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "message_revision_type_check": { + "name": "message_revision_type_check", + "value": "\"message_revisions\".\"revision_type\" in ('edit','delete')" + } + }, + "isRLSEnabled": false + }, + "public.message_saves": { + "name": "message_saves", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_saves_org_actor_idx": { + "name": "message_saves_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_saves_organisation_id_organisations_id_fk": { + "name": "message_saves_organisation_id_organisations_id_fk", + "tableFrom": "message_saves", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_saves_message_id_messages_id_fk": { + "name": "message_saves_message_id_messages_id_fk", + "tableFrom": "message_saves", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_saves_actor_id_actors_id_fk": { + "name": "message_saves_actor_id_actors_id_fk", + "tableFrom": "message_saves", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_saves_message_id_actor_id_pk": { + "name": "message_saves_message_id_actor_id_pk", + "columns": [ + "message_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "thread_parent_id": { + "name": "thread_parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_actor_id": { + "name": "author_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_type": { + "name": "message_type", + "type": "message_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "document": { + "name": "document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "plain_text": { + "name": "plain_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "edited_at": { + "name": "edited_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "data_classification": { + "name": "data_classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "related_alert_id": { + "name": "related_alert_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_agent_run_id": { + "name": "related_agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_workflow_run_id": { + "name": "related_workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "messages_org_room_time_idx": { + "name": "messages_org_room_time_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_thread_idx": { + "name": "messages_thread_idx", + "columns": [ + { + "expression": "thread_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_org_idempotency_unique": { + "name": "messages_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"messages\".\"idempotency_key\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_search_idx": { + "name": "messages_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"plain_text\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "messages_organisation_id_organisations_id_fk": { + "name": "messages_organisation_id_organisations_id_fk", + "tableFrom": "messages", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_room_id_rooms_id_fk": { + "name": "messages_room_id_rooms_id_fk", + "tableFrom": "messages", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_author_actor_id_actors_id_fk": { + "name": "messages_author_actor_id_actors_id_fk", + "tableFrom": "messages", + "tableTo": "actors", + "columnsFrom": [ + "author_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_related_alert_id_alerts_id_fk": { + "name": "messages_related_alert_id_alerts_id_fk", + "tableFrom": "messages", + "tableTo": "alerts", + "columnsFrom": [ + "related_alert_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_related_investigation_id_investigations_id_fk": { + "name": "messages_related_investigation_id_investigations_id_fk", + "tableFrom": "messages", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "safe_preview": { + "name": "safe_preview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target": { + "name": "target", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_org_actor_read_idx": { + "name": "notifications_org_actor_read_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "read_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_organisation_id_organisations_id_fk": { + "name": "notifications_organisation_id_organisations_id_fk", + "tableFrom": "notifications", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "notifications_actor_id_actors_id_fk": { + "name": "notifications_actor_id_actors_id_fk", + "tableFrom": "notifications", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organisations": { + "name": "organisations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data_region": { + "name": "data_region", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'australia'" + }, + "default_timezone": { + "name": "default_timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Australia/Sydney'" + }, + "retention_policy": { + "name": "retention_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "authentication_policy": { + "name": "authentication_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organisations_slug_unique": { + "name": "organisations_slug_unique", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organisations_status_check": { + "name": "organisations_status_check", + "value": "\"organisations\".\"status\" in ('active','suspended')" + } + }, + "isRLSEnabled": false + }, + "public.outbox_events": { + "name": "outbox_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aggregate_type": { + "name": "aggregate_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aggregate_id": { + "name": "aggregate_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "queue_name": { + "name": "queue_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "dispatched_at": { + "name": "dispatched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "outbox_idempotency_unique": { + "name": "outbox_idempotency_unique", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_pending_idx": { + "name": "outbox_pending_idx", + "columns": [ + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"outbox_events\".\"dispatched_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "outbox_events_organisation_id_organisations_id_fk": { + "name": "outbox_events_organisation_id_organisations_id_fk", + "tableFrom": "outbox_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reaction_operations": { + "name": "reaction_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "emoji": { + "name": "emoji", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "result_count": { + "name": "result_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_operations_org_idempotency_unique": { + "name": "reaction_operations_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_operations_org_message_idx": { + "name": "reaction_operations_org_message_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_operations_organisation_id_organisations_id_fk": { + "name": "reaction_operations_organisation_id_organisations_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_operations_message_id_messages_id_fk": { + "name": "reaction_operations_message_id_messages_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_operations_actor_id_actors_id_fk": { + "name": "reaction_operations_actor_id_actors_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reaction_pack_assets": { + "name": "reaction_pack_assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_id": { + "name": "revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alt_text": { + "name": "alt_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "byte_size": { + "name": "byte_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "frame_count": { + "name": "frame_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verification_state": { + "name": "verification_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'verified'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_pack_assets_org_revision_name_unique": { + "name": "reaction_pack_assets_org_revision_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_pack_assets_org_digest_idx": { + "name": "reaction_pack_assets_org_digest_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_pack_assets_organisation_id_organisations_id_fk": { + "name": "reaction_pack_assets_organisation_id_organisations_id_fk", + "tableFrom": "reaction_pack_assets", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_assets_revision_id_reaction_pack_revisions_id_fk": { + "name": "reaction_pack_assets_revision_id_reaction_pack_revisions_id_fk", + "tableFrom": "reaction_pack_assets", + "tableTo": "reaction_pack_revisions", + "columnsFrom": [ + "revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_pack_assets_verification_check": { + "name": "reaction_pack_assets_verification_check", + "value": "\"reaction_pack_assets\".\"verification_state\" in ('verified','missing','mismatch')" + }, + "reaction_pack_assets_dimensions_check": { + "name": "reaction_pack_assets_dimensions_check", + "value": "\"reaction_pack_assets\".\"width\" > 0 and \"reaction_pack_assets\".\"height\" > 0 and \"reaction_pack_assets\".\"frame_count\" > 0 and \"reaction_pack_assets\".\"byte_size\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.reaction_pack_revisions": { + "name": "reaction_pack_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pack_id": { + "name": "pack_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_by_actor_id": { + "name": "approved_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_pack_revisions_org_pack_revision_unique": { + "name": "reaction_pack_revisions_org_pack_revision_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pack_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_pack_revisions_org_status_idx": { + "name": "reaction_pack_revisions_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_pack_revisions_organisation_id_organisations_id_fk": { + "name": "reaction_pack_revisions_organisation_id_organisations_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_pack_id_reaction_packs_id_fk": { + "name": "reaction_pack_revisions_pack_id_reaction_packs_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "reaction_packs", + "columnsFrom": [ + "pack_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_approved_by_actor_id_actors_id_fk": { + "name": "reaction_pack_revisions_approved_by_actor_id_actors_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "actors", + "columnsFrom": [ + "approved_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_created_by_actor_id_actors_id_fk": { + "name": "reaction_pack_revisions_created_by_actor_id_actors_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_pack_revisions_status_check": { + "name": "reaction_pack_revisions_status_check", + "value": "\"reaction_pack_revisions\".\"status\" in ('draft','approved','superseded','removed')" + }, + "reaction_pack_revisions_revision_check": { + "name": "reaction_pack_revisions_revision_check", + "value": "\"reaction_pack_revisions\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.reaction_packs": { + "name": "reaction_packs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle": { + "name": "lifecycle", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "removed_by_actor_id": { + "name": "removed_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_packs_org_slug_unique": { + "name": "reaction_packs_org_slug_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_packs_org_lifecycle_idx": { + "name": "reaction_packs_org_lifecycle_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lifecycle", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_packs_organisation_id_organisations_id_fk": { + "name": "reaction_packs_organisation_id_organisations_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_packs_created_by_actor_id_actors_id_fk": { + "name": "reaction_packs_created_by_actor_id_actors_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_packs_removed_by_actor_id_actors_id_fk": { + "name": "reaction_packs_removed_by_actor_id_actors_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "actors", + "columnsFrom": [ + "removed_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_packs_lifecycle_check": { + "name": "reaction_packs_lifecycle_check", + "value": "\"reaction_packs\".\"lifecycle\" in ('active','removed')" + } + }, + "isRLSEnabled": false + }, + "public.reactions": { + "name": "reactions", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "emoji": { + "name": "emoji", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "reactions_organisation_id_organisations_id_fk": { + "name": "reactions_organisation_id_organisations_id_fk", + "tableFrom": "reactions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reactions_message_id_messages_id_fk": { + "name": "reactions_message_id_messages_id_fk", + "tableFrom": "reactions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reactions_actor_id_actors_id_fk": { + "name": "reactions_actor_id_actors_id_fk", + "tableFrom": "reactions", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "reactions_message_id_actor_id_emoji_pk": { + "name": "reactions_message_id_actor_id_emoji_pk", + "columns": [ + "message_id", + "actor_id", + "emoji" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.report_deliveries": { + "name": "report_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "report_id": { + "name": "report_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recipient": { + "name": "recipient", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'awaiting_approval'" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_deliveries_org_idempotency_unique": { + "name": "report_deliveries_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_deliveries_org_report_status_idx": { + "name": "report_deliveries_org_report_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "report_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_deliveries_organisation_id_organisations_id_fk": { + "name": "report_deliveries_organisation_id_organisations_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_report_id_report_manifests_id_fk": { + "name": "report_deliveries_report_id_report_manifests_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "report_manifests", + "columnsFrom": [ + "report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_approval_id_approvals_id_fk": { + "name": "report_deliveries_approval_id_approvals_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_requested_by_actor_id_actors_id_fk": { + "name": "report_deliveries_requested_by_actor_id_actors_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_deliveries_status_check": { + "name": "report_deliveries_status_check", + "value": "\"report_deliveries\".\"status\" in ('awaiting_approval','queued','delivered','failed','cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.report_manifests": { + "name": "report_manifests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "review_note": { + "name": "review_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "posted_message_id": { + "name": "posted_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_manifests_org_idempotency_unique": { + "name": "report_manifests_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_manifests_org_room_status_idx": { + "name": "report_manifests_org_room_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_manifests_organisation_id_organisations_id_fk": { + "name": "report_manifests_organisation_id_organisations_id_fk", + "tableFrom": "report_manifests", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_agent_run_id_agent_runs_id_fk": { + "name": "report_manifests_agent_run_id_agent_runs_id_fk", + "tableFrom": "report_manifests", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_task_id_tasks_id_fk": { + "name": "report_manifests_task_id_tasks_id_fk", + "tableFrom": "report_manifests", + "tableTo": "tasks", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_room_id_rooms_id_fk": { + "name": "report_manifests_room_id_rooms_id_fk", + "tableFrom": "report_manifests", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_requested_by_actor_id_actors_id_fk": { + "name": "report_manifests_requested_by_actor_id_actors_id_fk", + "tableFrom": "report_manifests", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_posted_message_id_messages_id_fk": { + "name": "report_manifests_posted_message_id_messages_id_fk", + "tableFrom": "report_manifests", + "tableTo": "messages", + "columnsFrom": [ + "posted_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_manifests_status_check": { + "name": "report_manifests_status_check", + "value": "\"report_manifests\".\"status\" in ('draft','reviewed','posted','superseded')" + }, + "report_manifests_version_check": { + "name": "report_manifests_version_check", + "value": "\"report_manifests\".\"version\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.report_schedules": { + "name": "report_schedules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cadence": { + "name": "cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "audience": { + "name": "audience", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'leadership'" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_schedules_org_idempotency_unique": { + "name": "report_schedules_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_schedules_org_due_idx": { + "name": "report_schedules_org_due_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_schedules_organisation_id_organisations_id_fk": { + "name": "report_schedules_organisation_id_organisations_id_fk", + "tableFrom": "report_schedules", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_schedules_room_id_rooms_id_fk": { + "name": "report_schedules_room_id_rooms_id_fk", + "tableFrom": "report_schedules", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_schedules_created_by_actor_id_actors_id_fk": { + "name": "report_schedules_created_by_actor_id_actors_id_fk", + "tableFrom": "report_schedules", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_schedules_cadence_check": { + "name": "report_schedules_cadence_check", + "value": "\"report_schedules\".\"cadence\" in ('weekly','monthly')" + }, + "report_schedules_audience_check": { + "name": "report_schedules_audience_check", + "value": "\"report_schedules\".\"audience\" in ('analyst','leadership','executive')" + } + }, + "isRLSEnabled": false + }, + "public.research_items": { + "name": "research_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watchlist_id": { + "name": "watchlist_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "research_run_id": { + "name": "research_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_published_at": { + "name": "source_published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "root_message_id": { + "name": "root_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "latest_message_id": { + "name": "latest_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "brief": { + "name": "brief", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feedback_by_actor_id": { + "name": "feedback_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "feedback_at": { + "name": "feedback_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_items_org_fingerprint_unique": { + "name": "research_items_org_fingerprint_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_items_org_watchlist_idx": { + "name": "research_items_org_watchlist_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "watchlist_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_items_organisation_id_organisations_id_fk": { + "name": "research_items_organisation_id_organisations_id_fk", + "tableFrom": "research_items", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_watchlist_id_research_watchlists_id_fk": { + "name": "research_items_watchlist_id_research_watchlists_id_fk", + "tableFrom": "research_items", + "tableTo": "research_watchlists", + "columnsFrom": [ + "watchlist_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_research_run_id_research_runs_id_fk": { + "name": "research_items_research_run_id_research_runs_id_fk", + "tableFrom": "research_items", + "tableTo": "research_runs", + "columnsFrom": [ + "research_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_root_message_id_messages_id_fk": { + "name": "research_items_root_message_id_messages_id_fk", + "tableFrom": "research_items", + "tableTo": "messages", + "columnsFrom": [ + "root_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_latest_message_id_messages_id_fk": { + "name": "research_items_latest_message_id_messages_id_fk", + "tableFrom": "research_items", + "tableTo": "messages", + "columnsFrom": [ + "latest_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_feedback_by_actor_id_actors_id_fk": { + "name": "research_items_feedback_by_actor_id_actors_id_fk", + "tableFrom": "research_items", + "tableTo": "actors", + "columnsFrom": [ + "feedback_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.research_runs": { + "name": "research_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watchlist_id": { + "name": "watchlist_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "source_limit": { + "name": "source_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_budget": { + "name": "token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cost_limit_cents": { + "name": "cost_limit_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "time_limit_seconds": { + "name": "time_limit_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_runs_org_idempotency_unique": { + "name": "research_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_runs_org_agent_unique": { + "name": "research_runs_org_agent_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_runs_org_status_idx": { + "name": "research_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_runs_organisation_id_organisations_id_fk": { + "name": "research_runs_organisation_id_organisations_id_fk", + "tableFrom": "research_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_runs_watchlist_id_research_watchlists_id_fk": { + "name": "research_runs_watchlist_id_research_watchlists_id_fk", + "tableFrom": "research_runs", + "tableTo": "research_watchlists", + "columnsFrom": [ + "watchlist_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_runs_agent_run_id_agent_runs_id_fk": { + "name": "research_runs_agent_run_id_agent_runs_id_fk", + "tableFrom": "research_runs", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "research_runs_status_check": { + "name": "research_runs_status_check", + "value": "\"research_runs\".\"status\" in ('queued','running','completed','failed')" + } + }, + "isRLSEnabled": false + }, + "public.research_watchlists": { + "name": "research_watchlists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vendors": { + "name": "vendors", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "technologies": { + "name": "technologies", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "sources": { + "name": "sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "cadence_minutes": { + "name": "cadence_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 240 + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_watchlists_org_name_unique": { + "name": "research_watchlists_org_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_watchlists_due_idx": { + "name": "research_watchlists_due_idx", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_watchlists_organisation_id_organisations_id_fk": { + "name": "research_watchlists_organisation_id_organisations_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_watchlists_room_id_rooms_id_fk": { + "name": "research_watchlists_room_id_rooms_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_watchlists_created_by_actor_id_actors_id_fk": { + "name": "research_watchlists_created_by_actor_id_actors_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "research_watchlists_cadence_check": { + "name": "research_watchlists_cadence_check", + "value": "\"research_watchlists\".\"cadence_minutes\" between 15 and 10080" + } + }, + "isRLSEnabled": false + }, + "public.room_integration_bindings": { + "name": "room_integration_bindings", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "room_integration_bindings_org_room_idx": { + "name": "room_integration_bindings_org_room_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_integration_bindings_organisation_id_organisations_id_fk": { + "name": "room_integration_bindings_organisation_id_organisations_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_room_id_rooms_id_fk": { + "name": "room_integration_bindings_room_id_rooms_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_integration_id_integration_records_id_fk": { + "name": "room_integration_bindings_integration_id_integration_records_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_created_by_actor_id_actors_id_fk": { + "name": "room_integration_bindings_created_by_actor_id_actors_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "room_integration_bindings_room_id_integration_id_pk": { + "name": "room_integration_bindings_room_id_integration_id_pk", + "columns": [ + "room_id", + "integration_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.room_invitations": { + "name": "room_invitations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invited_actor_id": { + "name": "invited_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_expires_at": { + "name": "access_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invited_by_actor_id": { + "name": "invited_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "responded_at": { + "name": "responded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "room_invitations_org_idempotency_unique": { + "name": "room_invitations_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "room_invitations_org_room_status_idx": { + "name": "room_invitations_org_room_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_invitations_organisation_id_organisations_id_fk": { + "name": "room_invitations_organisation_id_organisations_id_fk", + "tableFrom": "room_invitations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_room_id_rooms_id_fk": { + "name": "room_invitations_room_id_rooms_id_fk", + "tableFrom": "room_invitations", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_invited_actor_id_actors_id_fk": { + "name": "room_invitations_invited_actor_id_actors_id_fk", + "tableFrom": "room_invitations", + "tableTo": "actors", + "columnsFrom": [ + "invited_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_invited_by_actor_id_actors_id_fk": { + "name": "room_invitations_invited_by_actor_id_actors_id_fk", + "tableFrom": "room_invitations", + "tableTo": "actors", + "columnsFrom": [ + "invited_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "room_invitation_role_check": { + "name": "room_invitation_role_check", + "value": "\"room_invitations\".\"membership_role\" in ('moderator','member','guest','agent_member')" + }, + "room_invitation_status_check": { + "name": "room_invitation_status_check", + "value": "\"room_invitations\".\"status\" in ('pending','accepted','revoked','expired')" + } + }, + "isRLSEnabled": false + }, + "public.room_memberships": { + "name": "room_memberships", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notification_level": { + "name": "notification_level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "notify_replies": { + "name": "notify_replies", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "notify_followed_threads": { + "name": "notify_followed_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_read_event_id": { + "name": "last_read_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "muted": { + "name": "muted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "favourite": { + "name": "favourite", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sidebar_position": { + "name": "sidebar_position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sidebar_group": { + "name": "sidebar_group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_expires_at": { + "name": "access_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "room_memberships_org_actor_idx": { + "name": "room_memberships_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_memberships_organisation_id_organisations_id_fk": { + "name": "room_memberships_organisation_id_organisations_id_fk", + "tableFrom": "room_memberships", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_memberships_room_id_rooms_id_fk": { + "name": "room_memberships_room_id_rooms_id_fk", + "tableFrom": "room_memberships", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_memberships_actor_id_actors_id_fk": { + "name": "room_memberships_actor_id_actors_id_fk", + "tableFrom": "room_memberships", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "room_memberships_room_id_actor_id_pk": { + "name": "room_memberships_room_id_actor_id_pk", + "columns": [ + "room_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "room_membership_role_check": { + "name": "room_membership_role_check", + "value": "\"room_memberships\".\"membership_role\" in ('owner','moderator','member','guest','agent_member')" + } + }, + "isRLSEnabled": false + }, + "public.rooms": { + "name": "rooms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "room_type": { + "name": "room_type", + "type": "room_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'organisation'" + }, + "topic": { + "name": "topic", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "policies": { + "name": "policies", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "direct_fingerprint": { + "name": "direct_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "linked_investigation_id": { + "name": "linked_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "linked_kelpie_case_id": { + "name": "linked_kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_severity": { + "name": "default_severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "tlp": { + "name": "tlp", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'amber'" + }, + "retention_policy": { + "name": "retention_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "rooms_org_slug_unique": { + "name": "rooms_org_slug_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rooms_org_direct_fingerprint_unique": { + "name": "rooms_org_direct_fingerprint_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "direct_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"rooms\".\"direct_fingerprint\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "rooms_org_type_idx": { + "name": "rooms_org_type_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "rooms_organisation_id_organisations_id_fk": { + "name": "rooms_organisation_id_organisations_id_fk", + "tableFrom": "rooms", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "rooms_created_by_actor_id_actors_id_fk": { + "name": "rooms_created_by_actor_id_actors_id_fk", + "tableFrom": "rooms", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.synthetic_cleanup_receipts": { + "name": "synthetic_cleanup_receipts", + "schema": "", + "columns": { + "manifest_id": { + "name": "manifest_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "maintenance_actor_id": { + "name": "maintenance_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "manifest_digest": { + "name": "manifest_digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "candidate_counts": { + "name": "candidate_counts", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "pre_digests": { + "name": "pre_digests", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "post_digests": { + "name": "post_digests", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "object_storage_objects": { + "name": "object_storage_objects", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "synthetic_cleanup_receipts_approval_unique": { + "name": "synthetic_cleanup_receipts_approval_unique", + "columns": [ + { + "expression": "approval_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "synthetic_cleanup_receipts_org_digest_unique": { + "name": "synthetic_cleanup_receipts_org_digest_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "manifest_digest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "synthetic_cleanup_receipts_organisation_id_organisations_id_fk": { + "name": "synthetic_cleanup_receipts_organisation_id_organisations_id_fk", + "tableFrom": "synthetic_cleanup_receipts", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "synthetic_cleanup_receipts_approval_id_approvals_id_fk": { + "name": "synthetic_cleanup_receipts_approval_id_approvals_id_fk", + "tableFrom": "synthetic_cleanup_receipts", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "synthetic_cleanup_receipts_maintenance_actor_id_actors_id_fk": { + "name": "synthetic_cleanup_receipts_maintenance_actor_id_actors_id_fk", + "tableFrom": "synthetic_cleanup_receipts", + "tableTo": "actors", + "columnsFrom": [ + "maintenance_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tasks": { + "name": "tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "task_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'backlog'" + }, + "priority": { + "name": "priority", + "type": "task_priority", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "assigned_actor_id": { + "name": "assigned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approval_required": { + "name": "approval_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "due_at": { + "name": "due_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_run_status": { + "name": "agent_run_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tasks_org_status_idx": { + "name": "tasks_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tasks_org_assignee_idx": { + "name": "tasks_org_assignee_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "assigned_actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tasks_org_idempotency_unique": { + "name": "tasks_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tasks_organisation_id_organisations_id_fk": { + "name": "tasks_organisation_id_organisations_id_fk", + "tableFrom": "tasks", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_assigned_actor_id_actors_id_fk": { + "name": "tasks_assigned_actor_id_actors_id_fk", + "tableFrom": "tasks", + "tableTo": "actors", + "columnsFrom": [ + "assigned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_created_by_actor_id_actors_id_fk": { + "name": "tasks_created_by_actor_id_actors_id_fk", + "tableFrom": "tasks", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_room_id_rooms_id_fk": { + "name": "tasks_room_id_rooms_id_fk", + "tableFrom": "tasks", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_investigation_id_investigations_id_fk": { + "name": "tasks_investigation_id_investigations_id_fk", + "tableFrom": "tasks", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_follows": { + "name": "thread_follows", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "root_message_id": { + "name": "root_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "thread_follows_org_actor_idx": { + "name": "thread_follows_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "thread_follows_organisation_id_organisations_id_fk": { + "name": "thread_follows_organisation_id_organisations_id_fk", + "tableFrom": "thread_follows", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "thread_follows_root_message_id_messages_id_fk": { + "name": "thread_follows_root_message_id_messages_id_fk", + "tableFrom": "thread_follows", + "tableTo": "messages", + "columnsFrom": [ + "root_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "thread_follows_actor_id_actors_id_fk": { + "name": "thread_follows_actor_id_actors_id_fk", + "tableFrom": "thread_follows", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "thread_follows_root_message_id_actor_id_pk": { + "name": "thread_follows_root_message_id_actor_id_pk", + "columns": [ + "root_message_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.timeline_events": { + "name": "timeline_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "external_case_id": { + "name": "external_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "timeline_org_investigation_time_idx": { + "name": "timeline_org_investigation_time_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "timeline_events_organisation_id_organisations_id_fk": { + "name": "timeline_events_organisation_id_organisations_id_fk", + "tableFrom": "timeline_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_investigation_id_investigations_id_fk": { + "name": "timeline_events_investigation_id_investigations_id_fk", + "tableFrom": "timeline_events", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_room_id_rooms_id_fk": { + "name": "timeline_events_room_id_rooms_id_fk", + "tableFrom": "timeline_events", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_actor_id_actors_id_fk": { + "name": "timeline_events_actor_id_actors_id_fk", + "tableFrom": "timeline_events", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "better_auth_user_id": { + "name": "better_auth_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "job_title": { + "name": "job_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "team": { + "name": "team", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "presence_state": { + "name": "presence_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'offline'" + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Australia/Sydney'" + }, + "notification_preferences": { + "name": "notification_preferences", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_active_at": { + "name": "last_active_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_org_email_unique": { + "name": "users_org_email_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_better_auth_unique": { + "name": "users_better_auth_unique", + "columns": [ + { + "expression": "better_auth_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_org_idx": { + "name": "users_org_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_organisation_id_organisations_id_fk": { + "name": "users_organisation_id_organisations_id_fk", + "tableFrom": "users", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "users_better_auth_user_id_auth_user_id_fk": { + "name": "users_better_auth_user_id_auth_user_id_fk", + "tableFrom": "users", + "tableTo": "auth_user", + "columnsFrom": [ + "better_auth_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_definitions": { + "name": "workflow_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_key": { + "name": "workflow_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "yaml": { + "name": "yaml", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parsed": { + "name": "parsed", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "owner_actor_id": { + "name": "owner_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_defs_org_key_version_unique": { + "name": "workflow_defs_org_key_version_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_definitions_organisation_id_organisations_id_fk": { + "name": "workflow_definitions_organisation_id_organisations_id_fk", + "tableFrom": "workflow_definitions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_definitions_owner_actor_id_actors_id_fk": { + "name": "workflow_definitions_owner_actor_id_actors_id_fk", + "tableFrom": "workflow_definitions", + "tableTo": "actors", + "columnsFrom": [ + "owner_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_runs": { + "name": "workflow_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_definition_id": { + "name": "workflow_definition_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "trigger_event_id": { + "name": "trigger_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "workflow_runs_org_idempotency_unique": { + "name": "workflow_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_runs_organisation_id_organisations_id_fk": { + "name": "workflow_runs_organisation_id_organisations_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_workflow_definition_id_workflow_definitions_id_fk": { + "name": "workflow_runs_workflow_definition_id_workflow_definitions_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflow_definitions", + "columnsFrom": [ + "workflow_definition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_room_id_rooms_id_fk": { + "name": "workflow_runs_room_id_rooms_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_investigation_id_investigations_id_fk": { + "name": "workflow_runs_investigation_id_investigations_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_requested_by_actor_id_actors_id_fk": { + "name": "workflow_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.actor_type": { + "name": "actor_type", + "schema": "public", + "values": [ + "human", + "agent", + "product", + "service", + "system" + ] + }, + "public.alert_status": { + "name": "alert_status", + "schema": "public", + "values": [ + "new", + "acknowledged", + "investigating", + "dismissed", + "promoted", + "closed" + ] + }, + "public.approval_status": { + "name": "approval_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected", + "expired", + "cancelled", + "executed", + "failed" + ] + }, + "public.investigation_status": { + "name": "investigation_status", + "schema": "public", + "values": [ + "open", + "triaging", + "investigating", + "awaiting_approval", + "promoted", + "closed" + ] + }, + "public.message_type": { + "name": "message_type", + "schema": "public", + "values": [ + "text", + "system", + "alert", + "finding", + "decision", + "approval", + "workflow", + "agent-status", + "query-result", + "evidence", + "case-event", + "response-action" + ] + }, + "public.room_type": { + "name": "room_type", + "schema": "public", + "values": [ + "operations", + "incident", + "investigation", + "hunt", + "engineering", + "private", + "direct", + "system" + ] + }, + "public.severity": { + "name": "severity", + "schema": "public", + "values": [ + "critical", + "high", + "medium", + "low", + "informational" + ] + }, + "public.task_priority": { + "name": "task_priority", + "schema": "public", + "values": [ + "urgent", + "high", + "normal", + "low" + ] + }, + "public.task_status": { + "name": "task_status", + "schema": "public", + "values": [ + "backlog", + "ready", + "in_progress", + "review", + "done" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/database/migrations/meta/0019_snapshot.json b/packages/database/migrations/meta/0019_snapshot.json new file mode 100644 index 0000000..b78af12 --- /dev/null +++ b/packages/database/migrations/meta/0019_snapshot.json @@ -0,0 +1,11655 @@ +{ + "id": "19f6aac0-aa0b-471b-88d8-0a1f27531db9", + "prevId": "71b9ed46-7893-4ce6-ba96-f34a03d21ed0", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.actors": { + "name": "actors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "actor_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "identity_reference": { + "name": "identity_reference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "capability_assignments": { + "name": "capability_assignments", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "actors_org_type_idx": { + "name": "actors_org_type_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "actors_org_identity_unique": { + "name": "actors_org_identity_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "identity_reference", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"actors\".\"identity_reference\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "actors_organisation_id_organisations_id_fk": { + "name": "actors_organisation_id_organisations_id_fk", + "tableFrom": "actors", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_definitions": { + "name": "agent_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "runtime": { + "name": "runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_actor_id": { + "name": "owner_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "system_prompt_version": { + "name": "system_prompt_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "allowed_tools": { + "name": "allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "allowed_rooms": { + "name": "allowed_rooms", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "capability_requirements": { + "name": "capability_requirements", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "maximum_runtime_seconds": { + "name": "maximum_runtime_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 300 + }, + "maximum_token_budget": { + "name": "maximum_token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20000 + }, + "maximum_cost_cents": { + "name": "maximum_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 500 + }, + "data_classification_allowance": { + "name": "data_classification_allowance", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[\"internal\"]'::jsonb" + }, + "approval_requirements": { + "name": "approval_requirements", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "requested_permission_mode": { + "name": "requested_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'read_only'" + }, + "kill_switch": { + "name": "kill_switch", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_definitions_org_name_unique": { + "name": "agent_definitions_org_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_definitions_organisation_id_organisations_id_fk": { + "name": "agent_definitions_organisation_id_organisations_id_fk", + "tableFrom": "agent_definitions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_definitions_owner_actor_id_actors_id_fk": { + "name": "agent_definitions_owner_actor_id_actors_id_fk", + "tableFrom": "agent_definitions", + "tableTo": "actors", + "columnsFrom": [ + "owner_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_definitions_requested_permission_check": { + "name": "agent_definitions_requested_permission_check", + "value": "\"agent_definitions\".\"requested_permission_mode\" in ('read_only','approval_gated')" + } + }, + "isRLSEnabled": false + }, + "public.agent_memories": { + "name": "agent_memories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "supersedes_memory_id": { + "name": "supersedes_memory_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reviewed_by_actor_id": { + "name": "reviewed_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_memories_org_agent_idx": { + "name": "agent_memories_org_agent_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_memories_organisation_id_organisations_id_fk": { + "name": "agent_memories_organisation_id_organisations_id_fk", + "tableFrom": "agent_memories", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_agent_id_agent_definitions_id_fk": { + "name": "agent_memories_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_memories", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_source_run_id_agent_runs_id_fk": { + "name": "agent_memories_source_run_id_agent_runs_id_fk", + "tableFrom": "agent_memories", + "tableTo": "agent_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_memories_reviewed_by_actor_id_actors_id_fk": { + "name": "agent_memories_reviewed_by_actor_id_actors_id_fk", + "tableFrom": "agent_memories", + "tableTo": "actors", + "columnsFrom": [ + "reviewed_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_memories_kind_check": { + "name": "agent_memories_kind_check", + "value": "\"agent_memories\".\"kind\" in ('fact','preference','lesson','failure','procedure_hint')" + }, + "agent_memories_status_check": { + "name": "agent_memories_status_check", + "value": "\"agent_memories\".\"status\" in ('active','superseded','expired','rejected')" + }, + "agent_memories_confidence_check": { + "name": "agent_memories_confidence_check", + "value": "\"agent_memories\".\"confidence\" between 0 and 100" + } + }, + "isRLSEnabled": false + }, + "public.agent_readiness_snapshots": { + "name": "agent_readiness_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "process_identity": { + "name": "process_identity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "gateway_state": { + "name": "gateway_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authentication_state": { + "name": "authentication_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "observer_state": { + "name": "observer_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle_evidence_state": { + "name": "lifecycle_evidence_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle_state": { + "name": "lifecycle_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "capability_state": { + "name": "capability_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tool_state": { + "name": "tool_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_state": { + "name": "permission_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_runtime": { + "name": "reported_runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_provider": { + "name": "reported_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reported_model": { + "name": "reported_model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_capabilities": { + "name": "input_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "output_capabilities": { + "name": "output_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "available_commands": { + "name": "available_commands", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "tool_sources": { + "name": "tool_sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "tool_risk_classes": { + "name": "tool_risk_classes", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "requested_permission_mode": { + "name": "requested_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_permission_mode": { + "name": "effective_permission_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "limitations": { + "name": "limitations", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_readiness_org_agent_verified_idx": { + "name": "agent_readiness_org_agent_verified_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "verified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_readiness_org_process_verified_idx": { + "name": "agent_readiness_org_process_verified_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "process_identity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "verified_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_readiness_snapshots_organisation_id_organisations_id_fk": { + "name": "agent_readiness_snapshots_organisation_id_organisations_id_fk", + "tableFrom": "agent_readiness_snapshots", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_readiness_snapshots_agent_id_agent_definitions_id_fk": { + "name": "agent_readiness_snapshots_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_readiness_snapshots", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_readiness_evidence_states_check": { + "name": "agent_readiness_evidence_states_check", + "value": "\"agent_readiness_snapshots\".\"gateway_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"authentication_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"observer_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"lifecycle_evidence_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"capability_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"tool_state\" in ('reported','unavailable','unknown')\n and \"agent_readiness_snapshots\".\"permission_state\" in ('reported','unavailable','unknown')" + }, + "agent_readiness_lifecycle_state_check": { + "name": "agent_readiness_lifecycle_state_check", + "value": "\"agent_readiness_snapshots\".\"lifecycle_state\" in ('idle','running','stopped','failed','unknown')" + }, + "agent_readiness_permission_modes_check": { + "name": "agent_readiness_permission_modes_check", + "value": "\"agent_readiness_snapshots\".\"requested_permission_mode\" in ('read_only','approval_gated','unknown')\n and \"agent_readiness_snapshots\".\"effective_permission_mode\" in ('read_only','approval_gated','unknown')" + } + }, + "isRLSEnabled": false + }, + "public.agent_run_events": { + "name": "agent_run_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_run_events_org_run_idx": { + "name": "agent_run_events_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_events_organisation_id_organisations_id_fk": { + "name": "agent_run_events_organisation_id_organisations_id_fk", + "tableFrom": "agent_run_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_run_events_run_id_agent_runs_id_fk": { + "name": "agent_run_events_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_events", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_run_sources": { + "name": "agent_run_sources", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_run_sources_org_run_unique": { + "name": "agent_run_sources_org_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_run_sources_org_run_idx": { + "name": "agent_run_sources_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_run_sources_organisation_id_organisations_id_fk": { + "name": "agent_run_sources_organisation_id_organisations_id_fk", + "tableFrom": "agent_run_sources", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_run_sources_run_id_agent_runs_id_fk": { + "name": "agent_run_sources_run_id_agent_runs_id_fk", + "tableFrom": "agent_run_sources", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_runs": { + "name": "agent_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workflow_run_id": { + "name": "workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "request": { + "name": "request", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "progress": { + "name": "progress", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "heartbeat_at": { + "name": "heartbeat_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "lease_expires_at": { + "name": "lease_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deadline_at": { + "name": "deadline_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancellation_requested_at": { + "name": "cancellation_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "worker_id": { + "name": "worker_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "input_hash": { + "name": "input_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt_hash": { + "name": "prompt_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output_hash": { + "name": "output_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "output_schema": { + "name": "output_schema", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "runtime": { + "name": "runtime", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "maximum_runtime_seconds": { + "name": "maximum_runtime_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 300 + }, + "maximum_token_budget": { + "name": "maximum_token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20000 + }, + "maximum_cost_cents": { + "name": "maximum_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 500 + }, + "token_usage": { + "name": "token_usage", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "estimated_cost_cents": { + "name": "estimated_cost_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tool_call_count": { + "name": "tool_call_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "diagnostics": { + "name": "diagnostics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cancellation_reason": { + "name": "cancellation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "structured_output": { + "name": "structured_output", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "agent_runs_org_idempotency_unique": { + "name": "agent_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_runs_org_status_idx": { + "name": "agent_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_runs_recovery_idx": { + "name": "agent_runs_recovery_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lease_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_runs_agent_id_agent_definitions_id_fk": { + "name": "agent_runs_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_runs", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_organisation_id_organisations_id_fk": { + "name": "agent_runs_organisation_id_organisations_id_fk", + "tableFrom": "agent_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_room_id_rooms_id_fk": { + "name": "agent_runs_room_id_rooms_id_fk", + "tableFrom": "agent_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_investigation_id_investigations_id_fk": { + "name": "agent_runs_investigation_id_investigations_id_fk", + "tableFrom": "agent_runs", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_runs_requested_by_actor_id_actors_id_fk": { + "name": "agent_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "agent_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_skill_evaluations": { + "name": "agent_skill_evaluations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_version_id": { + "name": "skill_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "evaluator_actor_id": { + "name": "evaluator_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "suite": { + "name": "suite", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "passed": { + "name": "passed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "score": { + "name": "score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "baseline_score": { + "name": "baseline_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "regressions": { + "name": "regressions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skill_evaluations_version_idx": { + "name": "agent_skill_evaluations_version_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skill_evaluations_organisation_id_organisations_id_fk": { + "name": "agent_skill_evaluations_organisation_id_organisations_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_evaluations_skill_version_id_agent_skill_versions_id_fk": { + "name": "agent_skill_evaluations_skill_version_id_agent_skill_versions_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "agent_skill_versions", + "columnsFrom": [ + "skill_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_evaluations_evaluator_actor_id_actors_id_fk": { + "name": "agent_skill_evaluations_evaluator_actor_id_actors_id_fk", + "tableFrom": "agent_skill_evaluations", + "tableTo": "actors", + "columnsFrom": [ + "evaluator_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skill_evaluations_score_check": { + "name": "agent_skill_evaluations_score_check", + "value": "\"agent_skill_evaluations\".\"score\" between 0 and 100" + } + }, + "isRLSEnabled": false + }, + "public.agent_skill_versions": { + "name": "agent_skill_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_id": { + "name": "skill_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_run_id": { + "name": "source_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "based_on_version_id": { + "name": "based_on_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "change_rationale": { + "name": "change_rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "required_capabilities": { + "name": "required_capabilities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "allowed_tools": { + "name": "allowed_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'proposed'" + }, + "approved_by_actor_id": { + "name": "approved_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skill_versions_skill_version_unique": { + "name": "agent_skill_versions_skill_version_unique", + "columns": [ + { + "expression": "skill_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "agent_skill_versions_content_hash_unique": { + "name": "agent_skill_versions_content_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "content_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skill_versions_organisation_id_organisations_id_fk": { + "name": "agent_skill_versions_organisation_id_organisations_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_skill_id_agent_skills_id_fk": { + "name": "agent_skill_versions_skill_id_agent_skills_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "agent_skills", + "columnsFrom": [ + "skill_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_source_run_id_agent_runs_id_fk": { + "name": "agent_skill_versions_source_run_id_agent_runs_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "agent_runs", + "columnsFrom": [ + "source_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skill_versions_approved_by_actor_id_actors_id_fk": { + "name": "agent_skill_versions_approved_by_actor_id_actors_id_fk", + "tableFrom": "agent_skill_versions", + "tableTo": "actors", + "columnsFrom": [ + "approved_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skill_versions_state_check": { + "name": "agent_skill_versions_state_check", + "value": "\"agent_skill_versions\".\"state\" in ('proposed','evaluating','approved','rejected','published','rolled_back')" + } + }, + "isRLSEnabled": false + }, + "public.agent_skills": { + "name": "agent_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "skill_key": { + "name": "skill_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "active_version_id": { + "name": "active_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "agent_skills_org_agent_key_unique": { + "name": "agent_skills_org_agent_key_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "skill_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_skills_organisation_id_organisations_id_fk": { + "name": "agent_skills_organisation_id_organisations_id_fk", + "tableFrom": "agent_skills", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skills_agent_id_agent_definitions_id_fk": { + "name": "agent_skills_agent_id_agent_definitions_id_fk", + "tableFrom": "agent_skills", + "tableTo": "agent_definitions", + "columnsFrom": [ + "agent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_skills_created_by_actor_id_actors_id_fk": { + "name": "agent_skills_created_by_actor_id_actors_id_fk", + "tableFrom": "agent_skills", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_skills_status_check": { + "name": "agent_skills_status_check", + "value": "\"agent_skills\".\"status\" in ('draft','evaluating','published','retired')" + } + }, + "isRLSEnabled": false + }, + "public.agent_tool_calls": { + "name": "agent_tool_calls", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "run_id": { + "name": "run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tool_name": { + "name": "tool_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "capability": { + "name": "capability", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "arguments_hash": { + "name": "arguments_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "result_hash": { + "name": "result_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "agent_tool_calls_org_run_idx": { + "name": "agent_tool_calls_org_run_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_tool_calls_organisation_id_organisations_id_fk": { + "name": "agent_tool_calls_organisation_id_organisations_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_tool_calls_run_id_agent_runs_id_fk": { + "name": "agent_tool_calls_run_id_agent_runs_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "agent_runs", + "columnsFrom": [ + "run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "agent_tool_calls_approval_id_approvals_id_fk": { + "name": "agent_tool_calls_approval_id_approvals_id_fk", + "tableFrom": "agent_tool_calls", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.alerts": { + "name": "alerts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_product": { + "name": "source_product", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_instance": { + "name": "source_instance", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_reference": { + "name": "external_reference", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "alert_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'new'" + }, + "rule_name": { + "name": "rule_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rule_id": { + "name": "rule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "assigned_actor_id": { + "name": "assigned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "entities": { + "name": "entities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "observables": { + "name": "observables", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "raw_reference_metadata": { + "name": "raw_reference_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kelpie_case_id": { + "name": "kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "correlation_key": { + "name": "correlation_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "alerts_org_source_ref_unique": { + "name": "alerts_org_source_ref_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_product", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_instance", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_reference", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_org_dedupe_unique": { + "name": "alerts_org_dedupe_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_org_queue_idx": { + "name": "alerts_org_queue_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "severity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "received_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "alerts_search_idx": { + "name": "alerts_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"description\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "alerts_organisation_id_organisations_id_fk": { + "name": "alerts_organisation_id_organisations_id_fk", + "tableFrom": "alerts", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "alerts_assigned_actor_id_actors_id_fk": { + "name": "alerts_assigned_actor_id_actors_id_fk", + "tableFrom": "alerts", + "tableTo": "actors", + "columnsFrom": [ + "assigned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "alerts_room_id_rooms_id_fk": { + "name": "alerts_room_id_rooms_id_fk", + "tableFrom": "alerts", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.approvals": { + "name": "approvals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requesting_actor_id": { + "name": "requesting_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action_type": { + "name": "action_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "risk_summary": { + "name": "risk_summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "required_capability": { + "name": "required_capability", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "required_approval_count": { + "name": "required_approval_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "approval_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "decisions": { + "name": "decisions", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "decision_at": { + "name": "decision_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "executed_at": { + "name": "executed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "approvals_org_idempotency_unique": { + "name": "approvals_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "approvals_org_status_idx": { + "name": "approvals_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "approvals_organisation_id_organisations_id_fk": { + "name": "approvals_organisation_id_organisations_id_fk", + "tableFrom": "approvals", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "approvals_requesting_actor_id_actors_id_fk": { + "name": "approvals_requesting_actor_id_actors_id_fk", + "tableFrom": "approvals", + "tableTo": "actors", + "columnsFrom": [ + "requesting_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_events": { + "name": "audit_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_type": { + "name": "actor_type", + "type": "actor_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previous_hash": { + "name": "previous_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_hash": { + "name": "event_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_org_sequence_unique": { + "name": "audit_org_sequence_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_org_hash_unique": { + "name": "audit_org_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_org_target_idx": { + "name": "audit_org_target_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "audit_events_organisation_id_organisations_id_fk": { + "name": "audit_events_organisation_id_organisations_id_fk", + "tableFrom": "audit_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "audit_events_actor_id_actors_id_fk": { + "name": "audit_events_actor_id_actors_id_fk", + "tableFrom": "audit_events", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_account": { + "name": "auth_account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_accounts_user_idx": { + "name": "auth_accounts_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "auth_accounts_provider_unique": { + "name": "auth_accounts_provider_unique", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_account_user_id_auth_user_id_fk": { + "name": "auth_account_user_id_auth_user_id_fk", + "tableFrom": "auth_account", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_passkey": { + "name": "auth_passkey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_id": { + "name": "credential_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "device_type": { + "name": "device_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backed_up": { + "name": "backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "aaguid": { + "name": "aaguid", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_passkey_user_idx": { + "name": "auth_passkey_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_passkey_user_id_auth_user_id_fk": { + "name": "auth_passkey_user_id_auth_user_id_fk", + "tableFrom": "auth_passkey", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_passkey_credential_id_unique": { + "name": "auth_passkey_credential_id_unique", + "nullsNotDistinct": false, + "columns": [ + "credential_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_session": { + "name": "auth_session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_sessions_user_idx": { + "name": "auth_sessions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_session_user_id_auth_user_id_fk": { + "name": "auth_session_user_id_auth_user_id_fk", + "tableFrom": "auth_session", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_session_token_unique": { + "name": "auth_session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_two_factor": { + "name": "auth_two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "failed_verification_count": { + "name": "failed_verification_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "locked_until": { + "name": "locked_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auth_two_factor_user_unique": { + "name": "auth_two_factor_user_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auth_two_factor_user_id_auth_user_id_fk": { + "name": "auth_two_factor_user_id_auth_user_id_fk", + "tableFrom": "auth_two_factor", + "tableTo": "auth_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_user": { + "name": "auth_user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_user_email_unique": { + "name": "auth_user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth_verification": { + "name": "auth_verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "auth_verification_identifier_idx": { + "name": "auth_verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.decisions": { + "name": "decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "decision": { + "name": "decision", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decision_maker_actor_id": { + "name": "decision_maker_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "alternatives_considered": { + "name": "alternatives_considered", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "evidence_references": { + "name": "evidence_references", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_workflow_run_id": { + "name": "related_workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "decisions_org_investigation_idx": { + "name": "decisions_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "related_investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "decisions_organisation_id_organisations_id_fk": { + "name": "decisions_organisation_id_organisations_id_fk", + "tableFrom": "decisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_decision_maker_actor_id_actors_id_fk": { + "name": "decisions_decision_maker_actor_id_actors_id_fk", + "tableFrom": "decisions", + "tableTo": "actors", + "columnsFrom": [ + "decision_maker_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "decisions_related_investigation_id_investigations_id_fk": { + "name": "decisions_related_investigation_id_investigations_id_fk", + "tableFrom": "decisions", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.evidence": { + "name": "evidence", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_by_actor_id": { + "name": "uploaded_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "related_room_id": { + "name": "related_room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_timestamp": { + "name": "original_timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scan_state": { + "name": "scan_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "retention_state": { + "name": "retention_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "legal_hold": { + "name": "legal_hold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "object_lock_metadata": { + "name": "object_lock_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "evidence_org_hash_unique": { + "name": "evidence_org_hash_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "evidence_search_idx": { + "name": "evidence_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"file_name\" || ' ' || \"source\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "evidence_organisation_id_organisations_id_fk": { + "name": "evidence_organisation_id_organisations_id_fk", + "tableFrom": "evidence", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_uploaded_by_actor_id_actors_id_fk": { + "name": "evidence_uploaded_by_actor_id_actors_id_fk", + "tableFrom": "evidence", + "tableTo": "actors", + "columnsFrom": [ + "uploaded_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_related_room_id_rooms_id_fk": { + "name": "evidence_related_room_id_rooms_id_fk", + "tableFrom": "evidence", + "tableTo": "rooms", + "columnsFrom": [ + "related_room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "evidence_related_investigation_id_investigations_id_fk": { + "name": "evidence_related_investigation_id_investigations_id_fk", + "tableFrom": "evidence", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.findings": { + "name": "findings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "supporting_evidence": { + "name": "supporting_evidence", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_entities": { + "name": "related_entities", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "related_observables": { + "name": "related_observables", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "recommended_action": { + "name": "recommended_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_provenance": { + "name": "agent_provenance", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "human_reviewed_at": { + "name": "human_reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "findings_org_investigation_idx": { + "name": "findings_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "findings_search_idx": { + "name": "findings_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"summary\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "findings_organisation_id_organisations_id_fk": { + "name": "findings_organisation_id_organisations_id_fk", + "tableFrom": "findings", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "findings_investigation_id_investigations_id_fk": { + "name": "findings_investigation_id_investigations_id_fk", + "tableFrom": "findings", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "findings_created_by_actor_id_actors_id_fk": { + "name": "findings_created_by_actor_id_actors_id_fk", + "tableFrom": "findings", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hunt_queries": { + "name": "hunt_queries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "hunt_id": { + "name": "hunt_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "query_run_id": { + "name": "query_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_key": { + "name": "source_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "hunt_queries_org_query_run_unique": { + "name": "hunt_queries_org_query_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "query_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_queries_org_hunt_sequence_unique": { + "name": "hunt_queries_org_hunt_sequence_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hunt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_queries_org_hunt_idx": { + "name": "hunt_queries_org_hunt_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "hunt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sequence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hunt_queries_organisation_id_organisations_id_fk": { + "name": "hunt_queries_organisation_id_organisations_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_hunt_id_hunt_runs_id_fk": { + "name": "hunt_queries_hunt_id_hunt_runs_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "hunt_runs", + "columnsFrom": [ + "hunt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_integration_id_integration_records_id_fk": { + "name": "hunt_queries_integration_id_integration_records_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_template_id_integration_query_templates_id_fk": { + "name": "hunt_queries_template_id_integration_query_templates_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_query_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_queries_query_run_id_integration_query_runs_id_fk": { + "name": "hunt_queries_query_run_id_integration_query_runs_id_fk", + "tableFrom": "hunt_queries", + "tableTo": "integration_query_runs", + "columnsFrom": [ + "query_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.hunt_runs": { + "name": "hunt_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_message_id": { + "name": "source_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "linked_case_id": { + "name": "linked_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "training_mode": { + "name": "training_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "plan": { + "name": "plan", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'planned'" + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "hunt_runs_org_idempotency_unique": { + "name": "hunt_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_runs_org_agent_run_unique": { + "name": "hunt_runs_org_agent_run_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "hunt_runs_org_status_idx": { + "name": "hunt_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hunt_runs_organisation_id_organisations_id_fk": { + "name": "hunt_runs_organisation_id_organisations_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_agent_run_id_agent_runs_id_fk": { + "name": "hunt_runs_agent_run_id_agent_runs_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_task_id_tasks_id_fk": { + "name": "hunt_runs_task_id_tasks_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "tasks", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_source_message_id_messages_id_fk": { + "name": "hunt_runs_source_message_id_messages_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "messages", + "columnsFrom": [ + "source_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_room_id_rooms_id_fk": { + "name": "hunt_runs_room_id_rooms_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_requested_by_actor_id_actors_id_fk": { + "name": "hunt_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hunt_runs_approval_id_approvals_id_fk": { + "name": "hunt_runs_approval_id_approvals_id_fk", + "tableFrom": "hunt_runs", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "hunt_runs_status_check": { + "name": "hunt_runs_status_check", + "value": "\"hunt_runs\".\"status\" in ('planned','awaiting_approval','querying','analysing','completed','failed','cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.hypotheses": { + "name": "hypotheses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "statement": { + "name": "statement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "confidence": { + "name": "confidence", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 50 + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "supporting_finding_ids": { + "name": "supporting_finding_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "contradicting_finding_ids": { + "name": "contradicting_finding_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "hypotheses_org_investigation_idx": { + "name": "hypotheses_org_investigation_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "hypotheses_organisation_id_organisations_id_fk": { + "name": "hypotheses_organisation_id_organisations_id_fk", + "tableFrom": "hypotheses", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hypotheses_investigation_id_investigations_id_fk": { + "name": "hypotheses_investigation_id_investigations_id_fk", + "tableFrom": "hypotheses", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "hypotheses_created_by_actor_id_actors_id_fk": { + "name": "hypotheses_created_by_actor_id_actors_id_fk", + "tableFrom": "hypotheses", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.idempotency_records": { + "name": "idempotency_records", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "response_status": { + "name": "response_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "response_body": { + "name": "response_body", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idempotency_expiry_idx": { + "name": "idempotency_expiry_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "idempotency_records_organisation_id_organisations_id_fk": { + "name": "idempotency_records_organisation_id_organisations_id_fk", + "tableFrom": "idempotency_records", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "idempotency_records_organisation_id_scope_key_pk": { + "name": "idempotency_records_organisation_id_scope_key_pk", + "columns": [ + "organisation_id", + "scope", + "key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_connector_credentials": { + "name": "integration_connector_credentials", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "encrypted_credential": { + "name": "encrypted_credential", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "envelope_version": { + "name": "envelope_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'v1'" + }, + "rotation_version": { + "name": "rotation_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "rotated_by_actor_id": { + "name": "rotated_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_credentials_org_idx": { + "name": "integration_credentials_org_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_connector_credentials_organisation_id_organisations_id_fk": { + "name": "integration_connector_credentials_organisation_id_organisations_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_connector_credentials_integration_id_integration_records_id_fk": { + "name": "integration_connector_credentials_integration_id_integration_records_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_connector_credentials_rotated_by_actor_id_actors_id_fk": { + "name": "integration_connector_credentials_rotated_by_actor_id_actors_id_fk", + "tableFrom": "integration_connector_credentials", + "tableTo": "actors", + "columnsFrom": [ + "rotated_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_deliveries": { + "name": "integration_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "operation": { + "name": "operation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "request_metadata": { + "name": "request_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "response_metadata": { + "name": "response_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_delivery_org_idempotency_unique": { + "name": "integration_delivery_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_deliveries_organisation_id_organisations_id_fk": { + "name": "integration_deliveries_organisation_id_organisations_id_fk", + "tableFrom": "integration_deliveries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_deliveries_integration_id_integration_records_id_fk": { + "name": "integration_deliveries_integration_id_integration_records_id_fk", + "tableFrom": "integration_deliveries", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_entities": { + "name": "integration_entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "posture": { + "name": "posture", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "integration_entities_org_external_unique": { + "name": "integration_entities_org_external_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_entities_organisation_id_organisations_id_fk": { + "name": "integration_entities_organisation_id_organisations_id_fk", + "tableFrom": "integration_entities", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_entities_integration_id_integration_records_id_fk": { + "name": "integration_entities_integration_id_integration_records_id_fk", + "tableFrom": "integration_entities", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_query_runs": { + "name": "integration_query_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "input": { + "name": "input", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "request_metadata": { + "name": "request_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "response_metadata": { + "name": "response_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_query_runs_org_idempotency_unique": { + "name": "integration_query_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "integration_query_runs_org_status_idx": { + "name": "integration_query_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_query_runs_organisation_id_organisations_id_fk": { + "name": "integration_query_runs_organisation_id_organisations_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_integration_id_integration_records_id_fk": { + "name": "integration_query_runs_integration_id_integration_records_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_template_id_integration_query_templates_id_fk": { + "name": "integration_query_runs_template_id_integration_query_templates_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "integration_query_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_runs_requested_by_actor_id_actors_id_fk": { + "name": "integration_query_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "integration_query_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_query_templates": { + "name": "integration_query_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "template_key": { + "name": "template_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "definition": { + "name": "definition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integration_templates_org_key_version_unique": { + "name": "integration_templates_org_key_version_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_query_templates_organisation_id_organisations_id_fk": { + "name": "integration_query_templates_organisation_id_organisations_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_templates_integration_id_integration_records_id_fk": { + "name": "integration_query_templates_integration_id_integration_records_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "integration_query_templates_created_by_actor_id_actors_id_fk": { + "name": "integration_query_templates_created_by_actor_id_actors_id_fk", + "tableFrom": "integration_query_templates", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integration_records": { + "name": "integration_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mock": { + "name": "mock", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "configuration": { + "name": "configuration", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "health": { + "name": "health", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "cursor": { + "name": "cursor", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "integrations_org_product_instance_unique": { + "name": "integrations_org_product_instance_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "integration_records_organisation_id_organisations_id_fk": { + "name": "integration_records_organisation_id_organisations_id_fk", + "tableFrom": "integration_records", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.investigations": { + "name": "investigations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_number": { + "name": "investigation_number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "investigation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "severity": { + "name": "severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "lead_actor_id": { + "name": "lead_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_activity_at": { + "name": "last_activity_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "closed_at": { + "name": "closed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "recommendation": { + "name": "recommendation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "disposition": { + "name": "disposition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "promotion_decision": { + "name": "promotion_decision", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "linked_kelpie_case_id": { + "name": "linked_kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + } + }, + "indexes": { + "investigations_org_number_unique": { + "name": "investigations_org_number_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investigations_org_queue_idx": { + "name": "investigations_org_queue_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "severity", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_activity_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investigations_search_idx": { + "name": "investigations_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"title\" || ' ' || \"summary\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "investigations_organisation_id_organisations_id_fk": { + "name": "investigations_organisation_id_organisations_id_fk", + "tableFrom": "investigations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "investigations_lead_actor_id_actors_id_fk": { + "name": "investigations_lead_actor_id_actors_id_fk", + "tableFrom": "investigations", + "tableTo": "actors", + "columnsFrom": [ + "lead_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "investigations_room_id_rooms_id_fk": { + "name": "investigations_room_id_rooms_id_fk", + "tableFrom": "investigations", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message_mentions": { + "name": "message_mentions", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mentioned_actor_id": { + "name": "mentioned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "mention_type": { + "name": "mention_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mention_key": { + "name": "mention_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_mentions_org_actor_idx": { + "name": "message_mentions_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mentioned_actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_mentions_organisation_id_organisations_id_fk": { + "name": "message_mentions_organisation_id_organisations_id_fk", + "tableFrom": "message_mentions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_mentions_message_id_messages_id_fk": { + "name": "message_mentions_message_id_messages_id_fk", + "tableFrom": "message_mentions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_mentions_mentioned_actor_id_actors_id_fk": { + "name": "message_mentions_mentioned_actor_id_actors_id_fk", + "tableFrom": "message_mentions", + "tableTo": "actors", + "columnsFrom": [ + "mentioned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_mentions_message_id_mention_type_mention_key_pk": { + "name": "message_mentions_message_id_mention_type_mention_key_pk", + "columns": [ + "message_id", + "mention_type", + "mention_key" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "message_mention_type_check": { + "name": "message_mention_type_check", + "value": "\"message_mentions\".\"mention_type\" in ('actor','room','everyone')" + } + }, + "isRLSEnabled": false + }, + "public.message_pins": { + "name": "message_pins", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pinned_by_actor_id": { + "name": "pinned_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_pins_org_room_idx": { + "name": "message_pins_org_room_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_pins_organisation_id_organisations_id_fk": { + "name": "message_pins_organisation_id_organisations_id_fk", + "tableFrom": "message_pins", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_room_id_rooms_id_fk": { + "name": "message_pins_room_id_rooms_id_fk", + "tableFrom": "message_pins", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_message_id_messages_id_fk": { + "name": "message_pins_message_id_messages_id_fk", + "tableFrom": "message_pins", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_pins_pinned_by_actor_id_actors_id_fk": { + "name": "message_pins_pinned_by_actor_id_actors_id_fk", + "tableFrom": "message_pins", + "tableTo": "actors", + "columnsFrom": [ + "pinned_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_pins_room_id_message_id_pk": { + "name": "message_pins_room_id_message_id_pk", + "columns": [ + "room_id", + "message_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message_revisions": { + "name": "message_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_type": { + "name": "revision_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previous_document": { + "name": "previous_document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "previous_plain_text": { + "name": "previous_plain_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "next_document": { + "name": "next_document", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "next_plain_text": { + "name": "next_plain_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_revisions_org_message_idx": { + "name": "message_revisions_org_message_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "message_revisions_org_idempotency_unique": { + "name": "message_revisions_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_revisions_organisation_id_organisations_id_fk": { + "name": "message_revisions_organisation_id_organisations_id_fk", + "tableFrom": "message_revisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_revisions_message_id_messages_id_fk": { + "name": "message_revisions_message_id_messages_id_fk", + "tableFrom": "message_revisions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_revisions_actor_id_actors_id_fk": { + "name": "message_revisions_actor_id_actors_id_fk", + "tableFrom": "message_revisions", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "message_revision_type_check": { + "name": "message_revision_type_check", + "value": "\"message_revisions\".\"revision_type\" in ('edit','delete')" + } + }, + "isRLSEnabled": false + }, + "public.message_saves": { + "name": "message_saves", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_saves_org_actor_idx": { + "name": "message_saves_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_saves_organisation_id_organisations_id_fk": { + "name": "message_saves_organisation_id_organisations_id_fk", + "tableFrom": "message_saves", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_saves_message_id_messages_id_fk": { + "name": "message_saves_message_id_messages_id_fk", + "tableFrom": "message_saves", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "message_saves_actor_id_actors_id_fk": { + "name": "message_saves_actor_id_actors_id_fk", + "tableFrom": "message_saves", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "message_saves_message_id_actor_id_pk": { + "name": "message_saves_message_id_actor_id_pk", + "columns": [ + "message_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "thread_parent_id": { + "name": "thread_parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "author_actor_id": { + "name": "author_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_type": { + "name": "message_type", + "type": "message_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "document": { + "name": "document", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "plain_text": { + "name": "plain_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "edited_at": { + "name": "edited_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "data_classification": { + "name": "data_classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "related_alert_id": { + "name": "related_alert_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_investigation_id": { + "name": "related_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_agent_run_id": { + "name": "related_agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_workflow_run_id": { + "name": "related_workflow_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "messages_org_room_time_idx": { + "name": "messages_org_room_time_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_thread_idx": { + "name": "messages_thread_idx", + "columns": [ + { + "expression": "thread_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_org_idempotency_unique": { + "name": "messages_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"messages\".\"idempotency_key\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_search_idx": { + "name": "messages_search_idx", + "columns": [ + { + "expression": "to_tsvector('english', \"plain_text\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "messages_organisation_id_organisations_id_fk": { + "name": "messages_organisation_id_organisations_id_fk", + "tableFrom": "messages", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_room_id_rooms_id_fk": { + "name": "messages_room_id_rooms_id_fk", + "tableFrom": "messages", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_author_actor_id_actors_id_fk": { + "name": "messages_author_actor_id_actors_id_fk", + "tableFrom": "messages", + "tableTo": "actors", + "columnsFrom": [ + "author_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_related_alert_id_alerts_id_fk": { + "name": "messages_related_alert_id_alerts_id_fk", + "tableFrom": "messages", + "tableTo": "alerts", + "columnsFrom": [ + "related_alert_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "messages_related_investigation_id_investigations_id_fk": { + "name": "messages_related_investigation_id_investigations_id_fk", + "tableFrom": "messages", + "tableTo": "investigations", + "columnsFrom": [ + "related_investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "safe_preview": { + "name": "safe_preview", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target": { + "name": "target", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_org_actor_read_idx": { + "name": "notifications_org_actor_read_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "read_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_organisation_id_organisations_id_fk": { + "name": "notifications_organisation_id_organisations_id_fk", + "tableFrom": "notifications", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "notifications_actor_id_actors_id_fk": { + "name": "notifications_actor_id_actors_id_fk", + "tableFrom": "notifications", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organisations": { + "name": "organisations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data_region": { + "name": "data_region", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'australia'" + }, + "default_timezone": { + "name": "default_timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Australia/Sydney'" + }, + "retention_policy": { + "name": "retention_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "authentication_policy": { + "name": "authentication_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "organisations_slug_unique": { + "name": "organisations_slug_unique", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organisations_status_check": { + "name": "organisations_status_check", + "value": "\"organisations\".\"status\" in ('active','suspended')" + } + }, + "isRLSEnabled": false + }, + "public.outbox_events": { + "name": "outbox_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aggregate_type": { + "name": "aggregate_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aggregate_id": { + "name": "aggregate_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "queue_name": { + "name": "queue_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "available_at": { + "name": "available_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "dispatched_at": { + "name": "dispatched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "outbox_idempotency_unique": { + "name": "outbox_idempotency_unique", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "outbox_pending_idx": { + "name": "outbox_pending_idx", + "columns": [ + { + "expression": "available_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"outbox_events\".\"dispatched_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "outbox_events_organisation_id_organisations_id_fk": { + "name": "outbox_events_organisation_id_organisations_id_fk", + "tableFrom": "outbox_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reaction_operations": { + "name": "reaction_operations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "emoji": { + "name": "emoji", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "result_count": { + "name": "result_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_operations_org_idempotency_unique": { + "name": "reaction_operations_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_operations_org_message_idx": { + "name": "reaction_operations_org_message_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_operations_organisation_id_organisations_id_fk": { + "name": "reaction_operations_organisation_id_organisations_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_operations_message_id_messages_id_fk": { + "name": "reaction_operations_message_id_messages_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_operations_actor_id_actors_id_fk": { + "name": "reaction_operations_actor_id_actors_id_fk", + "tableFrom": "reaction_operations", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reaction_pack_assets": { + "name": "reaction_pack_assets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision_id": { + "name": "revision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alt_text": { + "name": "alt_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "byte_size": { + "name": "byte_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "frame_count": { + "name": "frame_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verification_state": { + "name": "verification_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'verified'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_pack_assets_org_revision_name_unique": { + "name": "reaction_pack_assets_org_revision_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_pack_assets_org_digest_idx": { + "name": "reaction_pack_assets_org_digest_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sha256", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_pack_assets_organisation_id_organisations_id_fk": { + "name": "reaction_pack_assets_organisation_id_organisations_id_fk", + "tableFrom": "reaction_pack_assets", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_assets_revision_id_reaction_pack_revisions_id_fk": { + "name": "reaction_pack_assets_revision_id_reaction_pack_revisions_id_fk", + "tableFrom": "reaction_pack_assets", + "tableTo": "reaction_pack_revisions", + "columnsFrom": [ + "revision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_pack_assets_verification_check": { + "name": "reaction_pack_assets_verification_check", + "value": "\"reaction_pack_assets\".\"verification_state\" in ('verified','missing','mismatch')" + }, + "reaction_pack_assets_dimensions_check": { + "name": "reaction_pack_assets_dimensions_check", + "value": "\"reaction_pack_assets\".\"width\" > 0 and \"reaction_pack_assets\".\"height\" > 0 and \"reaction_pack_assets\".\"frame_count\" > 0 and \"reaction_pack_assets\".\"byte_size\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.reaction_pack_revisions": { + "name": "reaction_pack_revisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pack_id": { + "name": "pack_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_by_actor_id": { + "name": "approved_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "superseded_at": { + "name": "superseded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_pack_revisions_org_pack_revision_unique": { + "name": "reaction_pack_revisions_org_pack_revision_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pack_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revision", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_pack_revisions_org_status_idx": { + "name": "reaction_pack_revisions_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_pack_revisions_organisation_id_organisations_id_fk": { + "name": "reaction_pack_revisions_organisation_id_organisations_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_pack_id_reaction_packs_id_fk": { + "name": "reaction_pack_revisions_pack_id_reaction_packs_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "reaction_packs", + "columnsFrom": [ + "pack_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_approved_by_actor_id_actors_id_fk": { + "name": "reaction_pack_revisions_approved_by_actor_id_actors_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "actors", + "columnsFrom": [ + "approved_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_pack_revisions_created_by_actor_id_actors_id_fk": { + "name": "reaction_pack_revisions_created_by_actor_id_actors_id_fk", + "tableFrom": "reaction_pack_revisions", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_pack_revisions_status_check": { + "name": "reaction_pack_revisions_status_check", + "value": "\"reaction_pack_revisions\".\"status\" in ('draft','approved','superseded','removed')" + }, + "reaction_pack_revisions_revision_check": { + "name": "reaction_pack_revisions_revision_check", + "value": "\"reaction_pack_revisions\".\"revision\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.reaction_packs": { + "name": "reaction_packs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lifecycle": { + "name": "lifecycle", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "removed_by_actor_id": { + "name": "removed_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reaction_packs_org_slug_unique": { + "name": "reaction_packs_org_slug_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reaction_packs_org_lifecycle_idx": { + "name": "reaction_packs_org_lifecycle_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "lifecycle", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reaction_packs_organisation_id_organisations_id_fk": { + "name": "reaction_packs_organisation_id_organisations_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_packs_created_by_actor_id_actors_id_fk": { + "name": "reaction_packs_created_by_actor_id_actors_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reaction_packs_removed_by_actor_id_actors_id_fk": { + "name": "reaction_packs_removed_by_actor_id_actors_id_fk", + "tableFrom": "reaction_packs", + "tableTo": "actors", + "columnsFrom": [ + "removed_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "reaction_packs_lifecycle_check": { + "name": "reaction_packs_lifecycle_check", + "value": "\"reaction_packs\".\"lifecycle\" in ('active','removed')" + } + }, + "isRLSEnabled": false + }, + "public.reactions": { + "name": "reactions", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "emoji": { + "name": "emoji", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "reactions_organisation_id_organisations_id_fk": { + "name": "reactions_organisation_id_organisations_id_fk", + "tableFrom": "reactions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reactions_message_id_messages_id_fk": { + "name": "reactions_message_id_messages_id_fk", + "tableFrom": "reactions", + "tableTo": "messages", + "columnsFrom": [ + "message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reactions_actor_id_actors_id_fk": { + "name": "reactions_actor_id_actors_id_fk", + "tableFrom": "reactions", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "reactions_message_id_actor_id_emoji_pk": { + "name": "reactions_message_id_actor_id_emoji_pk", + "columns": [ + "message_id", + "actor_id", + "emoji" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.report_deliveries": { + "name": "report_deliveries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "report_id": { + "name": "report_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recipient": { + "name": "recipient", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'awaiting_approval'" + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_deliveries_org_idempotency_unique": { + "name": "report_deliveries_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_deliveries_org_report_status_idx": { + "name": "report_deliveries_org_report_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "report_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_deliveries_organisation_id_organisations_id_fk": { + "name": "report_deliveries_organisation_id_organisations_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_report_id_report_manifests_id_fk": { + "name": "report_deliveries_report_id_report_manifests_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "report_manifests", + "columnsFrom": [ + "report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_approval_id_approvals_id_fk": { + "name": "report_deliveries_approval_id_approvals_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_deliveries_requested_by_actor_id_actors_id_fk": { + "name": "report_deliveries_requested_by_actor_id_actors_id_fk", + "tableFrom": "report_deliveries", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_deliveries_status_check": { + "name": "report_deliveries_status_check", + "value": "\"report_deliveries\".\"status\" in ('awaiting_approval','queued','delivered','failed','cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.report_manifests": { + "name": "report_manifests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "task_id": { + "name": "task_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'internal'" + }, + "review_note": { + "name": "review_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "posted_message_id": { + "name": "posted_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_manifests_org_idempotency_unique": { + "name": "report_manifests_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_manifests_org_room_status_idx": { + "name": "report_manifests_org_room_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_manifests_organisation_id_organisations_id_fk": { + "name": "report_manifests_organisation_id_organisations_id_fk", + "tableFrom": "report_manifests", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_agent_run_id_agent_runs_id_fk": { + "name": "report_manifests_agent_run_id_agent_runs_id_fk", + "tableFrom": "report_manifests", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_task_id_tasks_id_fk": { + "name": "report_manifests_task_id_tasks_id_fk", + "tableFrom": "report_manifests", + "tableTo": "tasks", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_room_id_rooms_id_fk": { + "name": "report_manifests_room_id_rooms_id_fk", + "tableFrom": "report_manifests", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_requested_by_actor_id_actors_id_fk": { + "name": "report_manifests_requested_by_actor_id_actors_id_fk", + "tableFrom": "report_manifests", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_manifests_posted_message_id_messages_id_fk": { + "name": "report_manifests_posted_message_id_messages_id_fk", + "tableFrom": "report_manifests", + "tableTo": "messages", + "columnsFrom": [ + "posted_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_manifests_status_check": { + "name": "report_manifests_status_check", + "value": "\"report_manifests\".\"status\" in ('draft','reviewed','posted','superseded')" + }, + "report_manifests_version_check": { + "name": "report_manifests_version_check", + "value": "\"report_manifests\".\"version\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.report_schedules": { + "name": "report_schedules", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cadence": { + "name": "cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "audience": { + "name": "audience", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'leadership'" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "report_schedules_org_idempotency_unique": { + "name": "report_schedules_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "report_schedules_org_due_idx": { + "name": "report_schedules_org_due_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_schedules_organisation_id_organisations_id_fk": { + "name": "report_schedules_organisation_id_organisations_id_fk", + "tableFrom": "report_schedules", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_schedules_room_id_rooms_id_fk": { + "name": "report_schedules_room_id_rooms_id_fk", + "tableFrom": "report_schedules", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "report_schedules_created_by_actor_id_actors_id_fk": { + "name": "report_schedules_created_by_actor_id_actors_id_fk", + "tableFrom": "report_schedules", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "report_schedules_cadence_check": { + "name": "report_schedules_cadence_check", + "value": "\"report_schedules\".\"cadence\" in ('weekly','monthly')" + }, + "report_schedules_audience_check": { + "name": "report_schedules_audience_check", + "value": "\"report_schedules\".\"audience\" in ('analyst','leadership','executive')" + } + }, + "isRLSEnabled": false + }, + "public.research_items": { + "name": "research_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watchlist_id": { + "name": "watchlist_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "research_run_id": { + "name": "research_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_published_at": { + "name": "source_published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "root_message_id": { + "name": "root_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "latest_message_id": { + "name": "latest_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "brief": { + "name": "brief", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feedback_by_actor_id": { + "name": "feedback_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "feedback_at": { + "name": "feedback_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_items_org_fingerprint_unique": { + "name": "research_items_org_fingerprint_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_items_org_watchlist_idx": { + "name": "research_items_org_watchlist_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "watchlist_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_items_organisation_id_organisations_id_fk": { + "name": "research_items_organisation_id_organisations_id_fk", + "tableFrom": "research_items", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_watchlist_id_research_watchlists_id_fk": { + "name": "research_items_watchlist_id_research_watchlists_id_fk", + "tableFrom": "research_items", + "tableTo": "research_watchlists", + "columnsFrom": [ + "watchlist_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_research_run_id_research_runs_id_fk": { + "name": "research_items_research_run_id_research_runs_id_fk", + "tableFrom": "research_items", + "tableTo": "research_runs", + "columnsFrom": [ + "research_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_root_message_id_messages_id_fk": { + "name": "research_items_root_message_id_messages_id_fk", + "tableFrom": "research_items", + "tableTo": "messages", + "columnsFrom": [ + "root_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_latest_message_id_messages_id_fk": { + "name": "research_items_latest_message_id_messages_id_fk", + "tableFrom": "research_items", + "tableTo": "messages", + "columnsFrom": [ + "latest_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_items_feedback_by_actor_id_actors_id_fk": { + "name": "research_items_feedback_by_actor_id_actors_id_fk", + "tableFrom": "research_items", + "tableTo": "actors", + "columnsFrom": [ + "feedback_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.research_runs": { + "name": "research_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "watchlist_id": { + "name": "watchlist_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "source_limit": { + "name": "source_limit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_budget": { + "name": "token_budget", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cost_limit_cents": { + "name": "cost_limit_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "time_limit_seconds": { + "name": "time_limit_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_runs_org_idempotency_unique": { + "name": "research_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_runs_org_agent_unique": { + "name": "research_runs_org_agent_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "agent_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_runs_org_status_idx": { + "name": "research_runs_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_runs_organisation_id_organisations_id_fk": { + "name": "research_runs_organisation_id_organisations_id_fk", + "tableFrom": "research_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_runs_watchlist_id_research_watchlists_id_fk": { + "name": "research_runs_watchlist_id_research_watchlists_id_fk", + "tableFrom": "research_runs", + "tableTo": "research_watchlists", + "columnsFrom": [ + "watchlist_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_runs_agent_run_id_agent_runs_id_fk": { + "name": "research_runs_agent_run_id_agent_runs_id_fk", + "tableFrom": "research_runs", + "tableTo": "agent_runs", + "columnsFrom": [ + "agent_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "research_runs_status_check": { + "name": "research_runs_status_check", + "value": "\"research_runs\".\"status\" in ('queued','running','completed','failed')" + } + }, + "isRLSEnabled": false + }, + "public.research_watchlists": { + "name": "research_watchlists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vendors": { + "name": "vendors", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "technologies": { + "name": "technologies", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "sources": { + "name": "sources", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "cadence_minutes": { + "name": "cadence_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 240 + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "research_watchlists_org_name_unique": { + "name": "research_watchlists_org_name_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "research_watchlists_due_idx": { + "name": "research_watchlists_due_idx", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "next_run_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "research_watchlists_organisation_id_organisations_id_fk": { + "name": "research_watchlists_organisation_id_organisations_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_watchlists_room_id_rooms_id_fk": { + "name": "research_watchlists_room_id_rooms_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "research_watchlists_created_by_actor_id_actors_id_fk": { + "name": "research_watchlists_created_by_actor_id_actors_id_fk", + "tableFrom": "research_watchlists", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "research_watchlists_cadence_check": { + "name": "research_watchlists_cadence_check", + "value": "\"research_watchlists\".\"cadence_minutes\" between 15 and 10080" + } + }, + "isRLSEnabled": false + }, + "public.room_integration_bindings": { + "name": "room_integration_bindings", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "integration_id": { + "name": "integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "room_integration_bindings_org_room_idx": { + "name": "room_integration_bindings_org_room_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_integration_bindings_organisation_id_organisations_id_fk": { + "name": "room_integration_bindings_organisation_id_organisations_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_room_id_rooms_id_fk": { + "name": "room_integration_bindings_room_id_rooms_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_integration_id_integration_records_id_fk": { + "name": "room_integration_bindings_integration_id_integration_records_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "integration_records", + "columnsFrom": [ + "integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_integration_bindings_created_by_actor_id_actors_id_fk": { + "name": "room_integration_bindings_created_by_actor_id_actors_id_fk", + "tableFrom": "room_integration_bindings", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "room_integration_bindings_room_id_integration_id_pk": { + "name": "room_integration_bindings_room_id_integration_id_pk", + "columns": [ + "room_id", + "integration_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.room_invitations": { + "name": "room_invitations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "invited_actor_id": { + "name": "invited_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_expires_at": { + "name": "access_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invited_by_actor_id": { + "name": "invited_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "responded_at": { + "name": "responded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "room_invitations_org_idempotency_unique": { + "name": "room_invitations_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "room_invitations_org_room_status_idx": { + "name": "room_invitations_org_room_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_invitations_organisation_id_organisations_id_fk": { + "name": "room_invitations_organisation_id_organisations_id_fk", + "tableFrom": "room_invitations", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_room_id_rooms_id_fk": { + "name": "room_invitations_room_id_rooms_id_fk", + "tableFrom": "room_invitations", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_invited_actor_id_actors_id_fk": { + "name": "room_invitations_invited_actor_id_actors_id_fk", + "tableFrom": "room_invitations", + "tableTo": "actors", + "columnsFrom": [ + "invited_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_invitations_invited_by_actor_id_actors_id_fk": { + "name": "room_invitations_invited_by_actor_id_actors_id_fk", + "tableFrom": "room_invitations", + "tableTo": "actors", + "columnsFrom": [ + "invited_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "room_invitation_role_check": { + "name": "room_invitation_role_check", + "value": "\"room_invitations\".\"membership_role\" in ('moderator','member','guest','agent_member')" + }, + "room_invitation_status_check": { + "name": "room_invitation_status_check", + "value": "\"room_invitations\".\"status\" in ('pending','accepted','revoked','expired')" + } + }, + "isRLSEnabled": false + }, + "public.room_memberships": { + "name": "room_memberships", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "membership_role": { + "name": "membership_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notification_level": { + "name": "notification_level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'all'" + }, + "notify_replies": { + "name": "notify_replies", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "notify_followed_threads": { + "name": "notify_followed_threads", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_read_event_id": { + "name": "last_read_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "muted": { + "name": "muted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "favourite": { + "name": "favourite", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sidebar_position": { + "name": "sidebar_position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sidebar_group": { + "name": "sidebar_group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_expires_at": { + "name": "access_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "room_memberships_org_actor_idx": { + "name": "room_memberships_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "room_memberships_organisation_id_organisations_id_fk": { + "name": "room_memberships_organisation_id_organisations_id_fk", + "tableFrom": "room_memberships", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_memberships_room_id_rooms_id_fk": { + "name": "room_memberships_room_id_rooms_id_fk", + "tableFrom": "room_memberships", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "room_memberships_actor_id_actors_id_fk": { + "name": "room_memberships_actor_id_actors_id_fk", + "tableFrom": "room_memberships", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "room_memberships_room_id_actor_id_pk": { + "name": "room_memberships_room_id_actor_id_pk", + "columns": [ + "room_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "room_membership_role_check": { + "name": "room_membership_role_check", + "value": "\"room_memberships\".\"membership_role\" in ('owner','moderator','member','guest','agent_member')" + } + }, + "isRLSEnabled": false + }, + "public.rooms": { + "name": "rooms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "room_type": { + "name": "room_type", + "type": "room_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'organisation'" + }, + "topic": { + "name": "topic", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "policies": { + "name": "policies", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "direct_fingerprint": { + "name": "direct_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "linked_investigation_id": { + "name": "linked_investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "linked_kelpie_case_id": { + "name": "linked_kelpie_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "default_severity": { + "name": "default_severity", + "type": "severity", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "tlp": { + "name": "tlp", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'amber'" + }, + "retention_policy": { + "name": "retention_policy", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "rooms_org_slug_unique": { + "name": "rooms_org_slug_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rooms_org_direct_fingerprint_unique": { + "name": "rooms_org_direct_fingerprint_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "direct_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"rooms\".\"direct_fingerprint\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "rooms_org_type_idx": { + "name": "rooms_org_type_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "room_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "rooms_organisation_id_organisations_id_fk": { + "name": "rooms_organisation_id_organisations_id_fk", + "tableFrom": "rooms", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "rooms_created_by_actor_id_actors_id_fk": { + "name": "rooms_created_by_actor_id_actors_id_fk", + "tableFrom": "rooms", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.synthetic_artifact_provenance": { + "name": "synthetic_artifact_provenance", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "artifact_table": { + "name": "artifact_table", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "artifact_id": { + "name": "artifact_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_reference": { + "name": "source_reference", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "recorded_by_actor_id": { + "name": "recorded_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "synthetic_artifact_provenance_artifact_unique": { + "name": "synthetic_artifact_provenance_artifact_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "artifact_table", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "artifact_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "synthetic_artifact_provenance_organisation_id_organisations_id_fk": { + "name": "synthetic_artifact_provenance_organisation_id_organisations_id_fk", + "tableFrom": "synthetic_artifact_provenance", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "synthetic_artifact_provenance_recorded_by_actor_id_actors_id_fk": { + "name": "synthetic_artifact_provenance_recorded_by_actor_id_actors_id_fk", + "tableFrom": "synthetic_artifact_provenance", + "tableTo": "actors", + "columnsFrom": [ + "recorded_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "synthetic_artifact_provenance_table_check": { + "name": "synthetic_artifact_provenance_table_check", + "value": "\"synthetic_artifact_provenance\".\"artifact_table\" in ('rooms','tasks','hunts','integrations','researchWatchlists','reportManifests','reportSchedules','messages','evidence','agentMemories','actors')" + }, + "synthetic_artifact_provenance_source_check": { + "name": "synthetic_artifact_provenance_source_check", + "value": "\"synthetic_artifact_provenance\".\"source_kind\" in ('seed_fixture','mock_runtime','test_fixture','legacy_live_proof')" + } + }, + "isRLSEnabled": false + }, + "public.synthetic_cleanup_object_deletion_attempts": { + "name": "synthetic_cleanup_object_deletion_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "manifest_id": { + "name": "manifest_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "evidence_id": { + "name": "evidence_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version_id": { + "name": "version_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authorization_approval_id": { + "name": "authorization_approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempted_by_actor_id": { + "name": "attempted_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "attempted_at": { + "name": "attempted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "synthetic_cleanup_object_attempts_manifest_idx": { + "name": "synthetic_cleanup_object_attempts_manifest_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "manifest_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attempted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "synthetic_cleanup_object_deletion_attempts_manifest_id_synthetic_cleanup_receipts_manifest_id_fk": { + "name": "synthetic_cleanup_object_deletion_attempts_manifest_id_synthetic_cleanup_receipts_manifest_id_fk", + "tableFrom": "synthetic_cleanup_object_deletion_attempts", + "tableTo": "synthetic_cleanup_receipts", + "columnsFrom": [ + "manifest_id" + ], + "columnsTo": [ + "manifest_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "synthetic_cleanup_object_deletion_attempts_organisation_id_organisations_id_fk": { + "name": "synthetic_cleanup_object_deletion_attempts_organisation_id_organisations_id_fk", + "tableFrom": "synthetic_cleanup_object_deletion_attempts", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "synthetic_cleanup_object_deletion_attempts_authorization_approval_id_approvals_id_fk": { + "name": "synthetic_cleanup_object_deletion_attempts_authorization_approval_id_approvals_id_fk", + "tableFrom": "synthetic_cleanup_object_deletion_attempts", + "tableTo": "approvals", + "columnsFrom": [ + "authorization_approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "synthetic_cleanup_object_deletion_attempts_attempted_by_actor_id_actors_id_fk": { + "name": "synthetic_cleanup_object_deletion_attempts_attempted_by_actor_id_actors_id_fk", + "tableFrom": "synthetic_cleanup_object_deletion_attempts", + "tableTo": "actors", + "columnsFrom": [ + "attempted_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "synthetic_cleanup_object_attempts_result_check": { + "name": "synthetic_cleanup_object_attempts_result_check", + "value": "\"synthetic_cleanup_object_deletion_attempts\".\"result\" in ('started','succeeded','failed','observed_missing')" + } + }, + "isRLSEnabled": false + }, + "public.synthetic_cleanup_receipts": { + "name": "synthetic_cleanup_receipts", + "schema": "", + "columns": { + "manifest_id": { + "name": "manifest_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "approval_id": { + "name": "approval_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "maintenance_actor_id": { + "name": "maintenance_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "manifest_digest": { + "name": "manifest_digest", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "manifest": { + "name": "manifest", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "candidate_counts": { + "name": "candidate_counts", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "pre_digests": { + "name": "pre_digests", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "post_digests": { + "name": "post_digests", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "object_storage_objects": { + "name": "object_storage_objects", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "synthetic_cleanup_receipts_approval_unique": { + "name": "synthetic_cleanup_receipts_approval_unique", + "columns": [ + { + "expression": "approval_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "synthetic_cleanup_receipts_org_digest_unique": { + "name": "synthetic_cleanup_receipts_org_digest_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "manifest_digest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "synthetic_cleanup_receipts_organisation_id_organisations_id_fk": { + "name": "synthetic_cleanup_receipts_organisation_id_organisations_id_fk", + "tableFrom": "synthetic_cleanup_receipts", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "synthetic_cleanup_receipts_approval_id_approvals_id_fk": { + "name": "synthetic_cleanup_receipts_approval_id_approvals_id_fk", + "tableFrom": "synthetic_cleanup_receipts", + "tableTo": "approvals", + "columnsFrom": [ + "approval_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "synthetic_cleanup_receipts_maintenance_actor_id_actors_id_fk": { + "name": "synthetic_cleanup_receipts_maintenance_actor_id_actors_id_fk", + "tableFrom": "synthetic_cleanup_receipts", + "tableTo": "actors", + "columnsFrom": [ + "maintenance_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tasks": { + "name": "tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "status": { + "name": "status", + "type": "task_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'backlog'" + }, + "priority": { + "name": "priority", + "type": "task_priority", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "assigned_actor_id": { + "name": "assigned_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_actor_id": { + "name": "created_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_case_id": { + "name": "related_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "approval_required": { + "name": "approval_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "due_at": { + "name": "due_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "agent_run_id": { + "name": "agent_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_run_status": { + "name": "agent_run_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tasks_org_status_idx": { + "name": "tasks_org_status_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tasks_org_assignee_idx": { + "name": "tasks_org_assignee_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "assigned_actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tasks_org_idempotency_unique": { + "name": "tasks_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tasks_organisation_id_organisations_id_fk": { + "name": "tasks_organisation_id_organisations_id_fk", + "tableFrom": "tasks", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_assigned_actor_id_actors_id_fk": { + "name": "tasks_assigned_actor_id_actors_id_fk", + "tableFrom": "tasks", + "tableTo": "actors", + "columnsFrom": [ + "assigned_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_created_by_actor_id_actors_id_fk": { + "name": "tasks_created_by_actor_id_actors_id_fk", + "tableFrom": "tasks", + "tableTo": "actors", + "columnsFrom": [ + "created_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_room_id_rooms_id_fk": { + "name": "tasks_room_id_rooms_id_fk", + "tableFrom": "tasks", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tasks_investigation_id_investigations_id_fk": { + "name": "tasks_investigation_id_investigations_id_fk", + "tableFrom": "tasks", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.thread_follows": { + "name": "thread_follows", + "schema": "", + "columns": { + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "root_message_id": { + "name": "root_message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "thread_follows_org_actor_idx": { + "name": "thread_follows_org_actor_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "thread_follows_organisation_id_organisations_id_fk": { + "name": "thread_follows_organisation_id_organisations_id_fk", + "tableFrom": "thread_follows", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "thread_follows_root_message_id_messages_id_fk": { + "name": "thread_follows_root_message_id_messages_id_fk", + "tableFrom": "thread_follows", + "tableTo": "messages", + "columnsFrom": [ + "root_message_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "thread_follows_actor_id_actors_id_fk": { + "name": "thread_follows_actor_id_actors_id_fk", + "tableFrom": "thread_follows", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "thread_follows_root_message_id_actor_id_pk": { + "name": "thread_follows_root_message_id_actor_id_pk", + "columns": [ + "root_message_id", + "actor_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.timeline_events": { + "name": "timeline_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "external_case_id": { + "name": "external_case_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_id": { + "name": "actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "timeline_org_investigation_time_idx": { + "name": "timeline_org_investigation_time_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "investigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "timeline_events_organisation_id_organisations_id_fk": { + "name": "timeline_events_organisation_id_organisations_id_fk", + "tableFrom": "timeline_events", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_investigation_id_investigations_id_fk": { + "name": "timeline_events_investigation_id_investigations_id_fk", + "tableFrom": "timeline_events", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_room_id_rooms_id_fk": { + "name": "timeline_events_room_id_rooms_id_fk", + "tableFrom": "timeline_events", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "timeline_events_actor_id_actors_id_fk": { + "name": "timeline_events_actor_id_actors_id_fk", + "tableFrom": "timeline_events", + "tableTo": "actors", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "better_auth_user_id": { + "name": "better_auth_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "job_title": { + "name": "job_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "team": { + "name": "team", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "presence_state": { + "name": "presence_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'offline'" + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Australia/Sydney'" + }, + "notification_preferences": { + "name": "notification_preferences", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_active_at": { + "name": "last_active_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_org_email_unique": { + "name": "users_org_email_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_better_auth_unique": { + "name": "users_better_auth_unique", + "columns": [ + { + "expression": "better_auth_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_org_idx": { + "name": "users_org_idx", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_organisation_id_organisations_id_fk": { + "name": "users_organisation_id_organisations_id_fk", + "tableFrom": "users", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "users_better_auth_user_id_auth_user_id_fk": { + "name": "users_better_auth_user_id_auth_user_id_fk", + "tableFrom": "users", + "tableTo": "auth_user", + "columnsFrom": [ + "better_auth_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_definitions": { + "name": "workflow_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_key": { + "name": "workflow_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "yaml": { + "name": "yaml", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parsed": { + "name": "parsed", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "owner_actor_id": { + "name": "owner_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_defs_org_key_version_unique": { + "name": "workflow_defs_org_key_version_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_definitions_organisation_id_organisations_id_fk": { + "name": "workflow_definitions_organisation_id_organisations_id_fk", + "tableFrom": "workflow_definitions", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_definitions_owner_actor_id_actors_id_fk": { + "name": "workflow_definitions_owner_actor_id_actors_id_fk", + "tableFrom": "workflow_definitions", + "tableTo": "actors", + "columnsFrom": [ + "owner_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_runs": { + "name": "workflow_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "organisation_id": { + "name": "organisation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workflow_definition_id": { + "name": "workflow_definition_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "trigger_event_id": { + "name": "trigger_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "room_id": { + "name": "room_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "investigation_id": { + "name": "investigation_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_actor_id": { + "name": "requested_by_actor_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "workflow_runs_org_idempotency_unique": { + "name": "workflow_runs_org_idempotency_unique", + "columns": [ + { + "expression": "organisation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_runs_organisation_id_organisations_id_fk": { + "name": "workflow_runs_organisation_id_organisations_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "organisations", + "columnsFrom": [ + "organisation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_workflow_definition_id_workflow_definitions_id_fk": { + "name": "workflow_runs_workflow_definition_id_workflow_definitions_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "workflow_definitions", + "columnsFrom": [ + "workflow_definition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_room_id_rooms_id_fk": { + "name": "workflow_runs_room_id_rooms_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "rooms", + "columnsFrom": [ + "room_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_investigation_id_investigations_id_fk": { + "name": "workflow_runs_investigation_id_investigations_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "investigations", + "columnsFrom": [ + "investigation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "workflow_runs_requested_by_actor_id_actors_id_fk": { + "name": "workflow_runs_requested_by_actor_id_actors_id_fk", + "tableFrom": "workflow_runs", + "tableTo": "actors", + "columnsFrom": [ + "requested_by_actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.actor_type": { + "name": "actor_type", + "schema": "public", + "values": [ + "human", + "agent", + "product", + "service", + "system" + ] + }, + "public.alert_status": { + "name": "alert_status", + "schema": "public", + "values": [ + "new", + "acknowledged", + "investigating", + "dismissed", + "promoted", + "closed" + ] + }, + "public.approval_status": { + "name": "approval_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected", + "expired", + "cancelled", + "executed", + "failed" + ] + }, + "public.investigation_status": { + "name": "investigation_status", + "schema": "public", + "values": [ + "open", + "triaging", + "investigating", + "awaiting_approval", + "promoted", + "closed" + ] + }, + "public.message_type": { + "name": "message_type", + "schema": "public", + "values": [ + "text", + "system", + "alert", + "finding", + "decision", + "approval", + "workflow", + "agent-status", + "query-result", + "evidence", + "case-event", + "response-action" + ] + }, + "public.room_type": { + "name": "room_type", + "schema": "public", + "values": [ + "operations", + "incident", + "investigation", + "hunt", + "engineering", + "private", + "direct", + "system" + ] + }, + "public.severity": { + "name": "severity", + "schema": "public", + "values": [ + "critical", + "high", + "medium", + "low", + "informational" + ] + }, + "public.task_priority": { + "name": "task_priority", + "schema": "public", + "values": [ + "urgent", + "high", + "normal", + "low" + ] + }, + "public.task_status": { + "name": "task_status", + "schema": "public", + "values": [ + "backlog", + "ready", + "in_progress", + "review", + "done" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/database/migrations/meta/_journal.json b/packages/database/migrations/meta/_journal.json index bd23923..3cc902a 100644 --- a/packages/database/migrations/meta/_journal.json +++ b/packages/database/migrations/meta/_journal.json @@ -120,6 +120,27 @@ "when": 1785115079134, "tag": "0016_sticky_ares", "breakpoints": true + }, + { + "idx": 17, + "version": "7", + "when": 1785126003887, + "tag": "0017_daily_dexter_bennett", + "breakpoints": true + }, + { + "idx": 18, + "version": "7", + "when": 1785126258301, + "tag": "0018_watery_morg", + "breakpoints": true + }, + { + "idx": 19, + "version": "7", + "when": 1785127721571, + "tag": "0019_fuzzy_zemo", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/database/package.json b/packages/database/package.json index 9b67978..d059000 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -29,10 +29,12 @@ "migrate": "tsx src/migrate.ts", "bootstrap": "tsx src/bootstrap.ts", "verify-clean": "tsx src/verify-clean-install.ts", + "verify:audit": "tsx src/verify-audit-integrity.ts", "seed": "tsx src/seed.ts" }, "dependencies": { "@muster/audit": "workspace:*", + "@muster/authz": "workspace:*", "@muster/contracts": "workspace:*", "drizzle-orm": "0.45.2", "pg": "^8.16.3", diff --git a/packages/database/src/domain-transaction.ts b/packages/database/src/domain-transaction.ts index 844f7ee..8150386 100644 --- a/packages/database/src/domain-transaction.ts +++ b/packages/database/src/domain-transaction.ts @@ -1,5 +1,5 @@ import { desc, eq, sql } from "drizzle-orm"; -import { hashAuditEvent } from "@muster/audit"; +import { hashAuditEvent, normaliseAuditMetadata } from "@muster/audit"; import type { ActorTypeSchema } from "@muster/contracts"; import type { z } from "zod"; import type { database } from "./index.ts"; @@ -38,6 +38,7 @@ export async function appendAuditEvent(tx: Transaction, input: AuditWrite) { const createdAt = new Date(); const sequence = (previous?.sequence ?? 0) + 1; const previousHash = previous?.eventHash ?? "0".repeat(64); + const metadata = normaliseAuditMetadata(input.metadata); const hashable = { organisationId: input.organisationId, sequence, @@ -47,7 +48,7 @@ export async function appendAuditEvent(tx: Transaction, input: AuditWrite) { targetType: input.targetType, targetId: input.targetId, previousHash, - metadata: input.metadata ?? {}, + metadata, traceId: input.traceId, createdAt: createdAt.toISOString(), }; @@ -56,7 +57,7 @@ export async function appendAuditEvent(tx: Transaction, input: AuditWrite) { await tx.insert(auditEvents).values({ id, ...input, - metadata: input.metadata ?? {}, + metadata, sequence, previousHash, eventHash, diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts index 6a3bd32..fd85b3f 100644 --- a/packages/database/src/index.ts +++ b/packages/database/src/index.ts @@ -26,3 +26,4 @@ export * from "./ids.ts"; export * from "./domain-transaction.ts"; export * from "./outbox.ts"; export * from "./repository.ts"; +export * from "./synthetic-cleanup.ts"; diff --git a/packages/database/src/schema.ts b/packages/database/src/schema.ts index 85b30a1..d69b4a7 100644 --- a/packages/database/src/schema.ts +++ b/packages/database/src/schema.ts @@ -1638,6 +1638,7 @@ export const tasks = pgTable( agentRunId: text("agent_run_id"), agentRunStatus: text("agent_run_status"), completedAt: timestamp("completed_at", { withTimezone: true }), + archivedAt: timestamp("archived_at", { withTimezone: true }), ...timestamps, }, (table) => [ @@ -1674,6 +1675,7 @@ export const integrationRecords = pgTable( health: jsonb("health").notNull().default({}), cursor: jsonb("cursor").notNull().default({}), lastSyncAt: timestamp("last_sync_at", { withTimezone: true }), + archivedAt: timestamp("archived_at", { withTimezone: true }), ...timestamps, }, (table) => [ @@ -1896,6 +1898,7 @@ export const huntRuns = pgTable( error: text("error"), idempotencyKey: text("idempotency_key").notNull(), completedAt: timestamp("completed_at", { withTimezone: true }), + archivedAt: timestamp("archived_at", { withTimezone: true }), ...timestamps, }, (table) => [ @@ -1968,9 +1971,15 @@ export const researchWatchlists = pgTable( "research_watchlists", { id: uuid("id").primaryKey(), - organisationId: uuid("organisation_id").notNull().references(() => organisations.id), - roomId: uuid("room_id").notNull().references(() => rooms.id), - createdByActorId: uuid("created_by_actor_id").notNull().references(() => actors.id), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), + roomId: uuid("room_id") + .notNull() + .references(() => rooms.id), + createdByActorId: uuid("created_by_actor_id") + .notNull() + .references(() => actors.id), name: text("name").notNull(), vendors: jsonb("vendors").notNull().default([]), technologies: jsonb("technologies").notNull().default([]), @@ -1979,12 +1988,19 @@ export const researchWatchlists = pgTable( enabled: boolean("enabled").notNull().default(true), nextRunAt: timestamp("next_run_at", { withTimezone: true }).notNull(), lastRunAt: timestamp("last_run_at", { withTimezone: true }), + archivedAt: timestamp("archived_at", { withTimezone: true }), ...timestamps, }, (table) => [ - uniqueIndex("research_watchlists_org_name_unique").on(table.organisationId, table.name), + uniqueIndex("research_watchlists_org_name_unique").on( + table.organisationId, + table.name, + ), index("research_watchlists_due_idx").on(table.enabled, table.nextRunAt), - check("research_watchlists_cadence_check", sql`${table.cadenceMinutes} between 15 and 10080`), + check( + "research_watchlists_cadence_check", + sql`${table.cadenceMinutes} between 15 and 10080`, + ), ], ); @@ -1992,9 +2008,15 @@ export const researchRuns = pgTable( "research_runs", { id: uuid("id").primaryKey(), - organisationId: uuid("organisation_id").notNull().references(() => organisations.id), - watchlistId: uuid("watchlist_id").notNull().references(() => researchWatchlists.id), - agentRunId: uuid("agent_run_id").notNull().references(() => agentRuns.id), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), + watchlistId: uuid("watchlist_id") + .notNull() + .references(() => researchWatchlists.id), + agentRunId: uuid("agent_run_id") + .notNull() + .references(() => agentRuns.id), status: text("status").notNull().default("queued"), sourceLimit: integer("source_limit").notNull(), tokenBudget: integer("token_budget").notNull(), @@ -2006,10 +2028,23 @@ export const researchRuns = pgTable( ...timestamps, }, (table) => [ - uniqueIndex("research_runs_org_idempotency_unique").on(table.organisationId, table.idempotencyKey), - uniqueIndex("research_runs_org_agent_unique").on(table.organisationId, table.agentRunId), - index("research_runs_org_status_idx").on(table.organisationId, table.status, table.createdAt), - check("research_runs_status_check", sql`${table.status} in ('queued','running','completed','failed')`), + uniqueIndex("research_runs_org_idempotency_unique").on( + table.organisationId, + table.idempotencyKey, + ), + uniqueIndex("research_runs_org_agent_unique").on( + table.organisationId, + table.agentRunId, + ), + index("research_runs_org_status_idx").on( + table.organisationId, + table.status, + table.createdAt, + ), + check( + "research_runs_status_check", + sql`${table.status} in ('queued','running','completed','failed')`, + ), ], ); @@ -2017,9 +2052,15 @@ export const researchItems = pgTable( "research_items", { id: uuid("id").primaryKey(), - organisationId: uuid("organisation_id").notNull().references(() => organisations.id), - watchlistId: uuid("watchlist_id").notNull().references(() => researchWatchlists.id), - researchRunId: uuid("research_run_id").notNull().references(() => researchRuns.id), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), + watchlistId: uuid("watchlist_id") + .notNull() + .references(() => researchWatchlists.id), + researchRunId: uuid("research_run_id") + .notNull() + .references(() => researchRuns.id), fingerprint: text("fingerprint").notNull(), sourceUrl: text("source_url").notNull(), sourcePublishedAt: timestamp("source_published_at", { withTimezone: true }), @@ -2032,8 +2073,15 @@ export const researchItems = pgTable( ...timestamps, }, (table) => [ - uniqueIndex("research_items_org_fingerprint_unique").on(table.organisationId, table.fingerprint), - index("research_items_org_watchlist_idx").on(table.organisationId, table.watchlistId, table.updatedAt), + uniqueIndex("research_items_org_fingerprint_unique").on( + table.organisationId, + table.fingerprint, + ), + index("research_items_org_watchlist_idx").on( + table.organisationId, + table.watchlistId, + table.updatedAt, + ), ], ); @@ -2043,11 +2091,17 @@ export const reportManifests = pgTable( "report_manifests", { id: uuid("id").primaryKey(), - organisationId: uuid("organisation_id").notNull().references(() => organisations.id), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), agentRunId: uuid("agent_run_id").references(() => agentRuns.id), taskId: uuid("task_id").references(() => tasks.id), - roomId: uuid("room_id").notNull().references(() => rooms.id), - requestedByActorId: uuid("requested_by_actor_id").notNull().references(() => actors.id), + roomId: uuid("room_id") + .notNull() + .references(() => rooms.id), + requestedByActorId: uuid("requested_by_actor_id") + .notNull() + .references(() => actors.id), version: integer("version").notNull().default(1), status: text("status").notNull().default("draft"), manifest: jsonb("manifest").notNull(), @@ -2056,12 +2110,24 @@ export const reportManifests = pgTable( postedMessageId: uuid("posted_message_id").references(() => messages.id), idempotencyKey: text("idempotency_key").notNull(), reviewedAt: timestamp("reviewed_at", { withTimezone: true }), + archivedAt: timestamp("archived_at", { withTimezone: true }), ...timestamps, }, (table) => [ - uniqueIndex("report_manifests_org_idempotency_unique").on(table.organisationId, table.idempotencyKey), - index("report_manifests_org_room_status_idx").on(table.organisationId, table.roomId, table.status, table.createdAt), - check("report_manifests_status_check", sql`${table.status} in ('draft','reviewed','posted','superseded')`), + uniqueIndex("report_manifests_org_idempotency_unique").on( + table.organisationId, + table.idempotencyKey, + ), + index("report_manifests_org_room_status_idx").on( + table.organisationId, + table.roomId, + table.status, + table.createdAt, + ), + check( + "report_manifests_status_check", + sql`${table.status} in ('draft','reviewed','posted','superseded')`, + ), check("report_manifests_version_check", sql`${table.version} > 0`), ], ); @@ -2070,10 +2136,18 @@ export const reportDeliveries = pgTable( "report_deliveries", { id: uuid("id").primaryKey(), - organisationId: uuid("organisation_id").notNull().references(() => organisations.id), - reportId: uuid("report_id").notNull().references(() => reportManifests.id), - approvalId: uuid("approval_id").notNull().references(() => approvals.id), - requestedByActorId: uuid("requested_by_actor_id").notNull().references(() => actors.id), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), + reportId: uuid("report_id") + .notNull() + .references(() => reportManifests.id), + approvalId: uuid("approval_id") + .notNull() + .references(() => approvals.id), + requestedByActorId: uuid("requested_by_actor_id") + .notNull() + .references(() => actors.id), recipient: text("recipient").notNull(), status: text("status").notNull().default("awaiting_approval"), result: jsonb("result"), @@ -2082,9 +2156,19 @@ export const reportDeliveries = pgTable( ...timestamps, }, (table) => [ - uniqueIndex("report_deliveries_org_idempotency_unique").on(table.organisationId, table.idempotencyKey), - index("report_deliveries_org_report_status_idx").on(table.organisationId, table.reportId, table.status), - check("report_deliveries_status_check", sql`${table.status} in ('awaiting_approval','queued','delivered','failed','cancelled')`), + uniqueIndex("report_deliveries_org_idempotency_unique").on( + table.organisationId, + table.idempotencyKey, + ), + index("report_deliveries_org_report_status_idx").on( + table.organisationId, + table.reportId, + table.status, + ), + check( + "report_deliveries_status_check", + sql`${table.status} in ('awaiting_approval','queued','delivered','failed','cancelled')`, + ), ], ); @@ -2092,9 +2176,15 @@ export const reportSchedules = pgTable( "report_schedules", { id: uuid("id").primaryKey(), - organisationId: uuid("organisation_id").notNull().references(() => organisations.id), - roomId: uuid("room_id").notNull().references(() => rooms.id), - createdByActorId: uuid("created_by_actor_id").notNull().references(() => actors.id), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), + roomId: uuid("room_id") + .notNull() + .references(() => rooms.id), + createdByActorId: uuid("created_by_actor_id") + .notNull() + .references(() => actors.id), cadence: text("cadence").notNull(), timezone: text("timezone").notNull(), audience: text("audience").notNull().default("leadership"), @@ -2102,13 +2192,27 @@ export const reportSchedules = pgTable( nextRunAt: timestamp("next_run_at", { withTimezone: true }).notNull(), lastRunAt: timestamp("last_run_at", { withTimezone: true }), idempotencyKey: text("idempotency_key").notNull(), + archivedAt: timestamp("archived_at", { withTimezone: true }), ...timestamps, }, (table) => [ - uniqueIndex("report_schedules_org_idempotency_unique").on(table.organisationId, table.idempotencyKey), - index("report_schedules_org_due_idx").on(table.organisationId, table.enabled, table.nextRunAt), - check("report_schedules_cadence_check", sql`${table.cadence} in ('weekly','monthly')`), - check("report_schedules_audience_check", sql`${table.audience} in ('analyst','leadership','executive')`), + uniqueIndex("report_schedules_org_idempotency_unique").on( + table.organisationId, + table.idempotencyKey, + ), + index("report_schedules_org_due_idx").on( + table.organisationId, + table.enabled, + table.nextRunAt, + ), + check( + "report_schedules_cadence_check", + sql`${table.cadence} in ('weekly','monthly')`, + ), + check( + "report_schedules_audience_check", + sql`${table.audience} in ('analyst','leadership','executive')`, + ), ], ); @@ -2134,6 +2238,114 @@ export const idempotencyRecords = pgTable( ], ); +export const syntheticArtifactProvenance = pgTable( + "synthetic_artifact_provenance", + { + id: uuid("id").primaryKey(), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), + artifactTable: text("artifact_table").notNull(), + artifactId: uuid("artifact_id").notNull(), + sourceKind: text("source_kind").notNull(), + sourceReference: text("source_reference").notNull(), + recordedByActorId: uuid("recorded_by_actor_id") + .notNull() + .references(() => actors.id), + recordedAt: timestamp("recorded_at", { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (table) => [ + uniqueIndex("synthetic_artifact_provenance_artifact_unique").on( + table.organisationId, + table.artifactTable, + table.artifactId, + ), + check( + "synthetic_artifact_provenance_table_check", + sql`${table.artifactTable} in ('rooms','tasks','hunts','integrations','researchWatchlists','reportManifests','reportSchedules','messages','evidence','agentMemories','actors')`, + ), + check( + "synthetic_artifact_provenance_source_check", + sql`${table.sourceKind} in ('seed_fixture','mock_runtime','test_fixture','legacy_live_proof')`, + ), + ], +); + +export const syntheticCleanupReceipts = pgTable( + "synthetic_cleanup_receipts", + { + manifestId: uuid("manifest_id").primaryKey(), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), + approvalId: uuid("approval_id") + .notNull() + .references(() => approvals.id), + maintenanceActorId: uuid("maintenance_actor_id") + .notNull() + .references(() => actors.id), + manifestDigest: text("manifest_digest").notNull(), + manifest: jsonb("manifest").notNull(), + candidateCounts: jsonb("candidate_counts").notNull(), + preDigests: jsonb("pre_digests").notNull(), + postDigests: jsonb("post_digests").notNull(), + objectStorageObjects: jsonb("object_storage_objects").notNull().default([]), + traceId: text("trace_id").notNull(), + appliedAt: timestamp("applied_at", { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (table) => [ + uniqueIndex("synthetic_cleanup_receipts_approval_unique").on( + table.approvalId, + ), + uniqueIndex("synthetic_cleanup_receipts_org_digest_unique").on( + table.organisationId, + table.manifestDigest, + ), + ], +); + +export const syntheticCleanupObjectDeletionAttempts = pgTable( + "synthetic_cleanup_object_deletion_attempts", + { + id: uuid("id").primaryKey(), + manifestId: uuid("manifest_id") + .notNull() + .references(() => syntheticCleanupReceipts.manifestId), + organisationId: uuid("organisation_id") + .notNull() + .references(() => organisations.id), + evidenceId: uuid("evidence_id").notNull(), + versionId: text("version_id").notNull(), + authorizationApprovalId: uuid("authorization_approval_id") + .notNull() + .references(() => approvals.id), + result: text("result").notNull(), + errorCode: text("error_code"), + attemptedByActorId: uuid("attempted_by_actor_id") + .notNull() + .references(() => actors.id), + traceId: text("trace_id").notNull(), + attemptedAt: timestamp("attempted_at", { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (table) => [ + index("synthetic_cleanup_object_attempts_manifest_idx").on( + table.organisationId, + table.manifestId, + table.attemptedAt, + ), + check( + "synthetic_cleanup_object_attempts_result_check", + sql`${table.result} in ('started','succeeded','failed','observed_missing')`, + ), + ], +); + export const outboxEvents = pgTable( "outbox_events", { diff --git a/packages/database/src/seed-data.test.ts b/packages/database/src/seed-data.test.ts new file mode 100644 index 0000000..f1e2cff --- /dev/null +++ b/packages/database/src/seed-data.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from "vitest"; +import { demoDirectRoomSeeds, demoIds, starterIds } from "./seed-data.ts"; + +function leafIds(value: unknown): string[] { + if (typeof value === "string") return [value]; + if (!value || typeof value !== "object") return []; + return Object.values(value).flatMap(leafIds); +} + +describe("demonstration direct rooms", () => { + it("seeds every declared direct-room ID with its intended membership", () => { + expect(demoDirectRoomSeeds.map((room) => room.id)).toEqual([ + demoIds.rooms.mayaDirect, + demoIds.rooms.triageDirect, + demoIds.rooms.tawnyDirect, + demoIds.rooms.parkerDirect, + ]); + expect(new Set(demoDirectRoomSeeds.map((room) => room.id)).size).toBe(4); + + expect(demoDirectRoomSeeds.map((room) => room.members).flat()).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + actorId: demoIds.actors.triage, + membershipRole: "agent_member", + }), + expect.objectContaining({ + actorId: demoIds.actors.tawnyHunt, + membershipRole: "agent_member", + }), + expect.objectContaining({ + actorId: demoIds.actors.threatIntel, + membershipRole: "agent_member", + }), + ]), + ); + }); + + it("cannot target the clean-install bootstrap organisation", () => { + const bootstrap = new Set(leafIds(starterIds)); + const demo = leafIds(demoIds); + + expect(demoIds.organisation).not.toBe(starterIds.organisation); + expect(new Set(demo).size).toBe(demo.length); + expect(demo.filter((id) => bootstrap.has(id))).toEqual([]); + }); +}); diff --git a/packages/database/src/seed-data.ts b/packages/database/src/seed-data.ts index a99aedf..3ad26b4 100644 --- a/packages/database/src/seed-data.ts +++ b/packages/database/src/seed-data.ts @@ -50,4 +50,110 @@ export const starterIds = { }, } as const; -export const demoIds = starterIds; +/** + * Deterministic IDs for demonstration data only. They must never overlap the + * clean-install bootstrap namespace above: demo seeding is opt-in and must not + * mutate a bootstrap workspace. + */ +export const demoIds = { + organisation: "019e7a10-0000-7000-8000-000000000001", + actors: { + jordan: "019e7a10-0000-7000-8000-000000000010", + maya: "019e7a10-0000-7000-8000-000000000011", + daniel: "019e7a10-0000-7000-8000-000000000012", + priya: "019e7a10-0000-7000-8000-000000000013", + alex: "019e7a10-0000-7000-8000-000000000014", + triage: "019e7a10-0000-7000-8000-000000000020", + tawnyHunt: "019e7a10-0000-7000-8000-000000000021", + bowerHealth: "019e7a10-0000-7000-8000-000000000022", + kelpieCase: "019e7a10-0000-7000-8000-000000000023", + sentinelQuery: "019e7a10-0000-7000-8000-000000000024", + threatIntel: "019e7a10-0000-7000-8000-000000000025", + system: "019e7a10-0000-7000-8000-000000000029", + }, + rooms: { + soc: "019e7a10-0000-7000-8000-000000000100", + activeIncidents: "019e7a10-0000-7000-8000-000000000101", + threatIntel: "019e7a10-0000-7000-8000-000000000102", + detection: "019e7a10-0000-7000-8000-000000000103", + endpoint: "019e7a10-0000-7000-8000-000000000104", + bower: "019e7a10-0000-7000-8000-000000000105", + incident: "019e7a10-0000-7000-8000-000000000106", + investigation: "019e7a10-0000-7000-8000-000000000107", + alerts: "019e7a10-0000-7000-8000-000000000108", + mayaDirect: "019e7a10-0000-7000-8000-000000000109", + triageDirect: "019e7a10-0000-7000-8000-000000000110", + tawnyDirect: "019e7a10-0000-7000-8000-000000000111", + parkerDirect: "019e7a10-0000-7000-8000-000000000112", + }, + investigation: "019e7a10-0000-7000-8000-000000000200", + alerts: { + tawny: "019e7a10-0000-7000-8000-000000000301", + bower: "019e7a10-0000-7000-8000-000000000302", + }, + approval: "019e7a10-0000-7000-8000-000000000401", + findings: { + encodedPowerShell: "019e7a10-0000-7000-8000-000000000501", + }, + integrations: { + kelpie: "019e7a10-0000-7000-8000-000000000601", + tawny: "019e7a10-0000-7000-8000-000000000602", + bower: "019e7a10-0000-7000-8000-000000000603", + }, + tasks: { + threatHunt: "019e7a10-0000-7000-8000-000000000801", + incidentEmail: "019e7a10-0000-7000-8000-000000000802", + executiveUpdate: "019e7a10-0000-7000-8000-000000000803", + monthlyLandscape: "019e7a10-0000-7000-8000-000000000804", + }, + messages: { + mayaParent: "019e7a10-0000-7000-8000-000000000701", + priyaReply: "019e7a10-0000-7000-8000-000000000702", + tawnyReply: "019e7a10-0000-7000-8000-000000000703", + justinReply: "019e7a10-0000-7000-8000-000000000704", + priyaParent: "019e7a10-0000-7000-8000-000000000705", + }, +} as const; + +/** + * Demonstration-only direct rooms. Keep this list aligned with the seed: the + * IDs are consumed by agent allow-lists and room memberships. + */ +export const demoDirectRoomSeeds = [ + { + id: demoIds.rooms.mayaDirect, + slug: "dm-maya-chen", + displayName: "Maya Chen", + members: [ + { actorId: demoIds.actors.jordan, membershipRole: "owner" }, + { actorId: demoIds.actors.maya, membershipRole: "member" }, + ], + }, + { + id: demoIds.rooms.triageDirect, + slug: "dm-triage-agent", + displayName: "Triage Agent", + members: [ + { actorId: demoIds.actors.jordan, membershipRole: "owner" }, + { actorId: demoIds.actors.triage, membershipRole: "agent_member" }, + ], + }, + { + id: demoIds.rooms.tawnyDirect, + slug: "dm-tawny-hunt-agent", + displayName: "Tawny Hunt Agent", + members: [ + { actorId: demoIds.actors.jordan, membershipRole: "owner" }, + { actorId: demoIds.actors.tawnyHunt, membershipRole: "agent_member" }, + ], + }, + { + id: demoIds.rooms.parkerDirect, + slug: "dm-parker", + displayName: "Parker", + members: [ + { actorId: demoIds.actors.jordan, membershipRole: "owner" }, + { actorId: demoIds.actors.threatIntel, membershipRole: "agent_member" }, + ], + }, +] as const; diff --git a/packages/database/src/seed.ts b/packages/database/src/seed.ts index 1e7dfe2..94cc238 100644 --- a/packages/database/src/seed.ts +++ b/packages/database/src/seed.ts @@ -1,5 +1,5 @@ import { database, closeDatabase, schema } from "./index.ts"; -import { demoIds } from "./seed-data.ts"; +import { demoDirectRoomSeeds, demoIds } from "./seed-data.ts"; import { sql } from "drizzle-orm"; if (process.env.MUSTER_DEMO_MODE !== "true") { @@ -266,7 +266,11 @@ await db model: process.env.MUSTER_CODEX_MODEL?.trim() || "configured", ownerActorId: demoIds.actors.jordan, systemPromptVersion: "alfie-v1", - allowedTools: ["alerts.read", "investigations.read", "research.feeds.read"], + allowedTools: [ + "alerts.read", + "investigations.read", + "research.feeds.read", + ], allowedRooms: [demoIds.rooms.soc, demoIds.rooms.triageDirect], capabilityRequirements: [ "alerts.read", @@ -324,7 +328,7 @@ await db "kelpie.cases.read", "audit.read", ], - allowedRooms: [demoIds.rooms.soc], + allowedRooms: [demoIds.rooms.soc, demoIds.rooms.parkerDirect], capabilityRequirements: [ "alerts.read", "investigations.read", @@ -391,16 +395,9 @@ const channelRoomRows = [ "investigation", ], ] as const; -const directRoomRows = [ - [demoIds.rooms.mayaDirect, "dm-maya-chen", "Maya Chen", "direct"], - [demoIds.rooms.triageDirect, "dm-triage-agent", "Triage Agent", "direct"], - [ - demoIds.rooms.tawnyDirect, - "dm-tawny-hunt-agent", - "Tawny Hunt Agent", - "direct", - ], -] as const; +const directRoomRows = demoDirectRoomSeeds.map( + ({ id, slug, displayName }) => [id, slug, displayName, "direct"] as const, +); const roomRows = [...channelRoomRows, ...directRoomRows] as const; await db .insert(schema.rooms) @@ -452,33 +449,14 @@ await db await db .insert(schema.roomMemberships) .values([ - ...[demoIds.actors.jordan, demoIds.actors.maya].map((actorId) => ({ - organisationId: demoIds.organisation, - roomId: demoIds.rooms.mayaDirect, - actorId, - membershipRole: - actorId === demoIds.actors.jordan - ? ("owner" as const) - : ("member" as const), - })), - ...[demoIds.actors.jordan, demoIds.actors.triage].map((actorId) => ({ - organisationId: demoIds.organisation, - roomId: demoIds.rooms.triageDirect, - actorId, - membershipRole: - actorId === demoIds.actors.triage - ? ("agent_member" as const) - : ("owner" as const), - })), - ...[demoIds.actors.jordan, demoIds.actors.tawnyHunt].map((actorId) => ({ - organisationId: demoIds.organisation, - roomId: demoIds.rooms.tawnyDirect, - actorId, - membershipRole: - actorId === demoIds.actors.tawnyHunt - ? ("agent_member" as const) - : ("owner" as const), - })), + ...demoDirectRoomSeeds.flatMap(({ id: roomId, members }) => + members.map(({ actorId, membershipRole }) => ({ + organisationId: demoIds.organisation, + roomId, + actorId, + membershipRole, + })), + ), ]) .onConflictDoNothing(); @@ -687,7 +665,7 @@ await db await db .insert(schema.findings) .values({ - id: "018f55d8-c4c7-7c3e-88ef-000000000501", + id: demoIds.findings.encodedPowerShell, organisationId: demoIds.organisation, investigationId: demoIds.investigation, createdByActorId: demoIds.actors.tawnyHunt, @@ -731,7 +709,7 @@ await db .insert(schema.integrationRecords) .values([ { - id: "018f55d8-c4c7-7c3e-88ef-000000000601", + id: demoIds.integrations.kelpie, organisationId: demoIds.organisation, product: "kelpie", instanceId: "kelpie-mock-au-01", @@ -740,7 +718,7 @@ await db mock: true, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000602", + id: demoIds.integrations.tawny, organisationId: demoIds.organisation, product: "tawny", instanceId: "tawny-mock-au-01", @@ -749,7 +727,7 @@ await db mock: true, }, { - id: "018f55d8-c4c7-7c3e-88ef-000000000603", + id: demoIds.integrations.bower, organisationId: demoIds.organisation, product: "bower", instanceId: "bower-mock-au-01", diff --git a/packages/database/src/synthetic-cleanup.integration.test.ts b/packages/database/src/synthetic-cleanup.integration.test.ts new file mode 100644 index 0000000..90e188f --- /dev/null +++ b/packages/database/src/synthetic-cleanup.integration.test.ts @@ -0,0 +1,932 @@ +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { and, eq, inArray } from "drizzle-orm"; +import { + applySyntheticCleanup, + authoriseSyntheticCleanupObjectRetry, + captureSyntheticCleanupManifest, + parseSyntheticCleanupManifest, + requestSyntheticCleanupApproval, + requestSyntheticCleanupObjectRetryApproval, + recordSyntheticCleanupObjectDeletionAttempt, + type SyntheticCleanupPlan, +} from "./synthetic-cleanup.ts"; +import { closeDatabase, database } from "./index.ts"; +import * as schema from "./schema.ts"; + +const integration = process.env.MUSTER_INTEGRATION_TESTS === "true"; +const describeIntegration = integration ? describe.sequential : describe.skip; + +const organisationId = "019fa300-0000-7000-8000-000000000001"; +const otherOrganisationId = "019fa300-0000-7000-8000-000000000002"; +const maintenanceActorId = "019fa300-0000-7000-8000-000000000003"; +const approverActorId = "019fa300-0000-7000-8000-000000000004"; +const syntheticActorId = "019fa300-0000-7000-8000-000000000005"; +const directRoomId = "019fa300-0000-7000-8000-000000000006"; +const candidateRoomId = "019fa300-0000-7000-8000-000000000007"; +const candidateTaskId = "019fa300-0000-7000-8000-000000000008"; +const heldEvidenceId = "019fa300-0000-7000-8000-000000000009"; +const candidateEvidenceId = "019fa300-0000-7000-8000-000000000010"; +const otherRoomId = "019fa300-0000-7000-8000-000000000011"; +const guardedRoomId = "019fa300-0000-7000-8000-000000000013"; +const protectedAgentId = "019fa300-0000-7000-8000-000000000014"; +const otherHumanActorId = "019fa300-0000-7000-8000-000000000012"; +const unprovenRoomId = "019fa300-0000-7000-8000-000000000015"; +const provenance = { + candidateRoom: "019fa303-0000-7000-8000-000000000001", + guardedRoom: "019fa303-0000-7000-8000-000000000002", + candidateTask: "019fa303-0000-7000-8000-000000000003", + heldEvidence: "019fa303-0000-7000-8000-000000000004", + candidateEvidence: "019fa303-0000-7000-8000-000000000005", + syntheticActor: "019fa303-0000-7000-8000-000000000006", +} as const; +const protectedMessageIds = [ + "019fa05a-fff0-76ce-9084-bf0707206d15", + "019fa05b-c62c-7368-8166-a23b68e3057f", + "019fa19f-335e-708e-9ce3-be4083921691", + "019fa19f-5c96-7402-8784-0324bb98d48c", +] as const; +const operatorSubject = { + actorId: maintenanceActorId, + organisationId, + capabilities: new Set(["administration.manage"] as const), +}; + +function basePlan( + suffix: string, + overrides: Partial = {}, +): SyntheticCleanupPlan { + return { + version: 2, + manifestId: `019fa301-0000-7000-8000-${suffix.padStart(12, "0")}`, + approvalId: `019fa302-0000-7000-8000-${suffix.padStart(12, "0")}`, + organisationId, + maintenanceActorId, + generatedAt: "2026-07-27T00:00:00.000Z", + archiveRoomIds: [], + archiveTaskIds: [], + archiveHuntIds: [], + archiveIntegrationIds: [], + archiveResearchWatchlistIds: [], + archiveReportManifestIds: [], + archiveReportScheduleIds: [], + hideMessageIds: [], + retireEvidenceIds: [], + rejectAgentMemoryIds: [], + retireActorIds: [], + selectionEvidence: [], + objectStorageObjects: [], + ...overrides, + }; +} + +describeIntegration("synthetic cleanup transaction", () => { + beforeAll(async () => { + const db = database(); + await db.insert(schema.organisations).values([ + { + id: organisationId, + name: "Synthetic Cleanup Integration", + slug: `synthetic-cleanup-${organisationId}`, + }, + { + id: otherOrganisationId, + name: "Protected Other Organisation", + slug: `synthetic-cleanup-${otherOrganisationId}`, + }, + ]); + await db.insert(schema.actors).values([ + { + id: maintenanceActorId, + organisationId, + actorType: "human", + displayName: "Synthetic Maintenance Operator", + identityReference: `synthetic-maintenance:${maintenanceActorId}`, + capabilityAssignments: ["administration.manage"], + }, + { + id: approverActorId, + organisationId, + actorType: "human", + displayName: "Synthetic Cleanup Approver", + identityReference: `synthetic-approver:${approverActorId}`, + capabilityAssignments: ["administration.manage"], + }, + { + id: syntheticActorId, + organisationId, + actorType: "human", + displayName: "Synthetic Retired User", + identityReference: `synthetic-user:${syntheticActorId}`, + capabilityAssignments: [], + }, + { + id: protectedAgentId, + organisationId: otherOrganisationId, + actorType: "agent", + displayName: "Protected Synthetic Agent", + identityReference: `agent:protected-fixture:${protectedAgentId}`, + capabilityAssignments: [], + }, + { + id: otherHumanActorId, + organisationId: otherOrganisationId, + actorType: "human", + displayName: "Protected Other User", + capabilityAssignments: [], + }, + ]); + await db.insert(schema.rooms).values([ + { + id: directRoomId, + organisationId: otherOrganisationId, + name: `protected-direct-${directRoomId}`, + slug: `protected-direct-${directRoomId}`, + displayName: "Protected direct room", + roomType: "direct", + visibility: "private", + createdByActorId: otherHumanActorId, + }, + { + id: candidateRoomId, + organisationId, + name: `synthetic-room-${candidateRoomId}`, + slug: `synthetic-room-${candidateRoomId}`, + displayName: "Synthetic cleanup room", + roomType: "operations", + createdByActorId: maintenanceActorId, + }, + { + id: guardedRoomId, + organisationId, + name: `synthetic-guarded-room-${guardedRoomId}`, + slug: `synthetic-guarded-room-${guardedRoomId}`, + displayName: "Synthetic guarded cleanup room", + roomType: "operations", + createdByActorId: maintenanceActorId, + }, + { + id: unprovenRoomId, + organisationId, + name: `demo-customer-room-${unprovenRoomId}`, + slug: `demo-customer-room-${unprovenRoomId}`, + displayName: "Genuine customer test coordination", + roomType: "operations", + visibility: "private", + createdByActorId: maintenanceActorId, + }, + ]); + await db.insert(schema.messages).values( + protectedMessageIds.map((id) => ({ + id, + organisationId: otherOrganisationId, + roomId: directRoomId, + authorActorId: otherHumanActorId, + messageType: "text" as const, + document: { type: "doc", content: [] }, + plainText: `Protected genuine fixture ${id}`, + idempotencyKey: `protected-fixture:${id}`, + })), + ); + await db.insert(schema.roomMemberships).values([ + { + organisationId: otherOrganisationId, + roomId: directRoomId, + actorId: otherHumanActorId, + membershipRole: "owner", + }, + { + organisationId: otherOrganisationId, + roomId: directRoomId, + actorId: protectedAgentId, + membershipRole: "agent_member", + }, + ]); + await db.insert(schema.tasks).values({ + id: candidateTaskId, + organisationId, + title: "Synthetic completed cleanup task", + status: "done", + createdByActorId: maintenanceActorId, + idempotencyKey: `synthetic-cleanup-task:${candidateTaskId}`, + completedAt: new Date("2026-07-27T00:00:00.000Z"), + }); + await db.insert(schema.evidence).values([ + { + id: heldEvidenceId, + organisationId, + fileName: "synthetic-held.bin", + mimeType: "application/octet-stream", + size: 1, + sha256: "b".repeat(64), + uploadedByActorId: maintenanceActorId, + classification: "internal", + source: "synthetic integration fixture", + storageKey: `synthetic/${heldEvidenceId}`, + legalHold: true, + }, + { + id: candidateEvidenceId, + organisationId, + fileName: "synthetic-candidate.bin", + mimeType: "application/octet-stream", + size: 1, + sha256: "c".repeat(64), + uploadedByActorId: maintenanceActorId, + classification: "internal", + source: "synthetic integration fixture", + storageKey: `synthetic/${candidateEvidenceId}`, + }, + ]); + await db.insert(schema.rooms).values({ + id: otherRoomId, + organisationId: otherOrganisationId, + name: `protected-other-${otherRoomId}`, + slug: `protected-other-${otherRoomId}`, + displayName: "Protected other room", + roomType: "operations", + createdByActorId: otherHumanActorId, + }); + await db.insert(schema.syntheticArtifactProvenance).values([ + { + id: provenance.candidateRoom, + organisationId, + artifactTable: "rooms", + artifactId: candidateRoomId, + sourceKind: "test_fixture", + sourceReference: "synthetic-cleanup-integration", + recordedByActorId: maintenanceActorId, + }, + { + id: provenance.guardedRoom, + organisationId, + artifactTable: "rooms", + artifactId: guardedRoomId, + sourceKind: "test_fixture", + sourceReference: "synthetic-cleanup-integration", + recordedByActorId: maintenanceActorId, + }, + { + id: provenance.candidateTask, + organisationId, + artifactTable: "tasks", + artifactId: candidateTaskId, + sourceKind: "test_fixture", + sourceReference: "synthetic-cleanup-integration", + recordedByActorId: maintenanceActorId, + }, + { + id: provenance.heldEvidence, + organisationId, + artifactTable: "evidence", + artifactId: heldEvidenceId, + sourceKind: "test_fixture", + sourceReference: "synthetic-cleanup-integration", + recordedByActorId: maintenanceActorId, + }, + { + id: provenance.candidateEvidence, + organisationId, + artifactTable: "evidence", + artifactId: candidateEvidenceId, + sourceKind: "test_fixture", + sourceReference: "synthetic-cleanup-integration", + recordedByActorId: maintenanceActorId, + }, + { + id: provenance.syntheticActor, + organisationId, + artifactTable: "actors", + artifactId: syntheticActorId, + sourceKind: "test_fixture", + sourceReference: "synthetic-cleanup-integration", + recordedByActorId: maintenanceActorId, + }, + ]); + }); + + afterAll(closeDatabase); + + it("fails closed for cross-organisation and legal-held candidates", async () => { + await expect( + captureSyntheticCleanupManifest( + operatorSubject, + basePlan("1", { + archiveRoomIds: [otherRoomId], + selectionEvidence: [ + { + table: "rooms", + recordId: otherRoomId, + provenanceId: provenance.candidateRoom, + }, + ], + }), + ), + ).rejects.toThrow("candidate count or ownership changed"); + + await expect( + captureSyntheticCleanupManifest( + operatorSubject, + basePlan("2", { + retireEvidenceIds: [heldEvidenceId], + selectionEvidence: [ + { + table: "evidence", + recordId: heldEvidenceId, + provenanceId: provenance.heldEvidence, + }, + ], + objectStorageObjects: [ + { + evidenceId: heldEvidenceId, + bucket: "synthetic-evidence", + key: `synthetic/${heldEvidenceId}`, + versionId: "synthetic-version-held", + etag: "synthetic-etag-held", + size: 1, + sha256: "b".repeat(64), + legalHold: false, + objectLockMetadata: {}, + }, + ], + }), + ), + ).rejects.toThrow("held or already retired"); + + await expect( + captureSyntheticCleanupManifest( + operatorSubject, + basePlan("5", { + archiveRoomIds: [unprovenRoomId], + selectionEvidence: [ + { + table: "rooms", + recordId: unprovenRoomId, + provenanceId: provenance.guardedRoom, + }, + ], + }), + ), + ).rejects.toThrow("lacks exact append-only synthetic provenance"); + + const [held] = await database() + .select({ state: schema.evidence.retentionState }) + .from(schema.evidence) + .where(eq(schema.evidence.id, heldEvidenceId)); + expect(held?.state).toBe("active"); + }); + + it("does not mutate for pending, stale, or capability-revoked approval", async () => { + const manifest = await captureSyntheticCleanupManifest( + operatorSubject, + basePlan("4", { + archiveRoomIds: [guardedRoomId], + selectionEvidence: [ + { + table: "rooms", + recordId: guardedRoomId, + provenanceId: provenance.guardedRoom, + }, + ], + }), + ); + const otherAdminSubject = { + actorId: approverActorId, + organisationId, + capabilities: new Set(["administration.manage"] as const), + }; + await expect( + requestSyntheticCleanupApproval( + otherAdminSubject, + manifest, + "trace-cleanup-impersonated-request", + ), + ).rejects.toThrow("Missing capability"); + await requestSyntheticCleanupApproval( + operatorSubject, + manifest, + "trace-cleanup-guard-request", + ); + await expect( + applySyntheticCleanup( + otherAdminSubject, + manifest, + "trace-cleanup-impersonated-apply", + ), + ).rejects.toThrow("Missing capability"); + await expect( + applySyntheticCleanup(operatorSubject, manifest, "trace-cleanup-pending"), + ).rejects.toThrow("approval is missing"); + + await database() + .update(schema.approvals) + .set({ + status: "approved", + decisions: [ + { + actorId: maintenanceActorId, + status: "approved", + reason: "Invalid self approval", + decidedAt: "2026-07-27T00:01:00.000Z", + }, + ], + }) + .where(eq(schema.approvals.id, manifest.approvalId)); + await expect( + applySyntheticCleanup( + operatorSubject, + manifest, + "trace-cleanup-self-approved", + ), + ).rejects.toThrow("independent approver"); + + await database() + .update(schema.approvals) + .set({ + decisions: [ + { + actorId: approverActorId, + status: "approved", + reason: "Exact guarded fixture reviewed", + decidedAt: "2026-07-27T00:02:00.000Z", + }, + ], + }) + .where(eq(schema.approvals.id, manifest.approvalId)); + await database() + .update(schema.actors) + .set({ capabilityAssignments: [] }) + .where(eq(schema.actors.id, approverActorId)); + await expect( + applySyntheticCleanup(operatorSubject, manifest, "trace-cleanup-revoked"), + ).rejects.toThrow("capability was revoked"); + + await database() + .update(schema.actors) + .set({ capabilityAssignments: ["administration.manage"] }) + .where(eq(schema.actors.id, approverActorId)); + await database() + .update(schema.approvals) + .set({ + target: { manifestId: manifest.manifestId, digest: "d".repeat(64) }, + }) + .where(eq(schema.approvals.id, manifest.approvalId)); + await expect( + applySyntheticCleanup( + operatorSubject, + manifest, + "trace-cleanup-target-drift", + ), + ).rejects.toThrow("approval is missing"); + + await database() + .update(schema.approvals) + .set({ + target: { + manifestId: manifest.manifestId, + digest: manifest.digest, + }, + }) + .where(eq(schema.approvals.id, manifest.approvalId)); + await database() + .update(schema.rooms) + .set({ topic: "Changed after exact manifest capture" }) + .where(eq(schema.rooms.id, guardedRoomId)); + await expect( + applySyntheticCleanup( + operatorSubject, + manifest, + "trace-cleanup-prestate-drift", + ), + ).rejects.toThrow("pre-state digest changed"); + await database() + .update(schema.rooms) + .set({ topic: "" }) + .where(eq(schema.rooms.id, guardedRoomId)); + await database() + .update(schema.approvals) + .set({ expiresAt: new Date("2026-07-26T00:00:00.000Z") }) + .where(eq(schema.approvals.id, manifest.approvalId)); + await expect( + applySyntheticCleanup(operatorSubject, manifest, "trace-cleanup-expired"), + ).rejects.toThrow("approval is missing"); + + const [room, receipt] = await Promise.all([ + database() + .select({ archivedAt: schema.rooms.archivedAt }) + .from(schema.rooms) + .where(eq(schema.rooms.id, guardedRoomId)), + database() + .select() + .from(schema.syntheticCleanupReceipts) + .where( + eq(schema.syntheticCleanupReceipts.manifestId, manifest.manifestId), + ), + ]); + expect(room[0]?.archivedAt).toBeNull(); + expect(receipt).toHaveLength(0); + }); + + it("applies the exact approved manifest once and preserves receipt/history", async () => { + const manifest = await captureSyntheticCleanupManifest( + operatorSubject, + basePlan("3", { + archiveRoomIds: [candidateRoomId], + archiveTaskIds: [candidateTaskId], + retireEvidenceIds: [candidateEvidenceId], + retireActorIds: [syntheticActorId], + selectionEvidence: [ + { + table: "rooms", + recordId: candidateRoomId, + provenanceId: provenance.candidateRoom, + }, + { + table: "tasks", + recordId: candidateTaskId, + provenanceId: provenance.candidateTask, + }, + { + table: "evidence", + recordId: candidateEvidenceId, + provenanceId: provenance.candidateEvidence, + }, + { + table: "actors", + recordId: syntheticActorId, + provenanceId: provenance.syntheticActor, + }, + ], + objectStorageObjects: [ + { + evidenceId: candidateEvidenceId, + bucket: "synthetic-evidence", + key: `synthetic/${candidateEvidenceId}`, + versionId: "synthetic-version-1", + etag: "synthetic-etag", + size: 1, + sha256: "c".repeat(64), + legalHold: false, + objectLockMetadata: {}, + }, + ], + }), + ); + const requested = await requestSyntheticCleanupApproval( + operatorSubject, + manifest, + "trace-cleanup-request", + ); + expect(requested).toMatchObject({ requested: true }); + const duplicateRequest = await requestSyntheticCleanupApproval( + operatorSubject, + manifest, + "trace-cleanup-request-replay", + ); + expect(duplicateRequest).toMatchObject({ requested: false }); + + await database() + .update(schema.approvals) + .set({ + status: "approved", + decisions: [ + { + actorId: approverActorId, + status: "approved", + reason: "Exact synthetic fixture reviewed", + decidedAt: "2026-07-27T00:01:00.000Z", + }, + ], + decisionAt: new Date("2026-07-27T00:01:00.000Z"), + }) + .where( + and( + eq(schema.approvals.organisationId, organisationId), + eq(schema.approvals.id, manifest.approvalId), + ), + ); + + const concurrentResults = await Promise.all([ + applySyntheticCleanup(operatorSubject, manifest, "trace-cleanup-apply-a"), + applySyntheticCleanup(operatorSubject, manifest, "trace-cleanup-apply-b"), + ]); + expect(concurrentResults.map((result) => result.applied).sort()).toEqual([ + false, + true, + ]); + expect(concurrentResults.find((result) => result.applied)).toMatchObject({ + applied: true, + candidateCounts: { + rooms: 1, + tasks: 1, + evidence: 1, + actors: 1, + }, + }); + + const [ + room, + task, + evidence, + actor, + approval, + receipts, + audits, + outbox, + protectedMessages, + ] = await Promise.all([ + database() + .select({ archivedAt: schema.rooms.archivedAt }) + .from(schema.rooms) + .where(eq(schema.rooms.id, candidateRoomId)), + database() + .select({ archivedAt: schema.tasks.archivedAt }) + .from(schema.tasks) + .where(eq(schema.tasks.id, candidateTaskId)), + database() + .select({ state: schema.evidence.retentionState }) + .from(schema.evidence) + .where(eq(schema.evidence.id, candidateEvidenceId)), + database() + .select({ status: schema.actors.status }) + .from(schema.actors) + .where(eq(schema.actors.id, syntheticActorId)), + database() + .select({ + status: schema.approvals.status, + executedAt: schema.approvals.executedAt, + }) + .from(schema.approvals) + .where(eq(schema.approvals.id, manifest.approvalId)), + database() + .select() + .from(schema.syntheticCleanupReceipts) + .where( + eq(schema.syntheticCleanupReceipts.manifestId, manifest.manifestId), + ), + database() + .select() + .from(schema.auditEvents) + .where( + and( + eq(schema.auditEvents.organisationId, organisationId), + eq(schema.auditEvents.targetId, manifest.manifestId), + ), + ), + database() + .select() + .from(schema.outboxEvents) + .where( + and( + eq(schema.outboxEvents.organisationId, organisationId), + eq(schema.outboxEvents.aggregateId, manifest.manifestId), + ), + ), + database() + .select({ + id: schema.messages.id, + deletedAt: schema.messages.deletedAt, + }) + .from(schema.messages) + .where( + and( + eq(schema.messages.organisationId, otherOrganisationId), + inArray(schema.messages.id, [...protectedMessageIds]), + ), + ), + ]); + expect(room[0]?.archivedAt).toBeInstanceOf(Date); + expect(task[0]?.archivedAt).toBeInstanceOf(Date); + expect(evidence[0]?.state).toBe("retired"); + expect(actor[0]?.status).toBe("inactive"); + expect(approval[0]).toMatchObject({ status: "executed" }); + expect(approval[0]?.executedAt).toBeInstanceOf(Date); + expect(receipts).toHaveLength(1); + expect(audits).toHaveLength(2); + expect(outbox).toHaveLength(3); + expect(outbox).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + queueName: "muster-maintenance", + eventType: "maintenance.synthetic_cleanup.object_delete.queued", + aggregateId: manifest.manifestId, + }), + ]), + ); + expect(protectedMessages).toHaveLength(4); + expect( + protectedMessages.every((message) => message.deletedAt === null), + ).toBe(true); + + let mutationError: unknown; + try { + await database() + .update(schema.syntheticCleanupReceipts) + .set({ traceId: "tampered" }) + .where( + eq(schema.syntheticCleanupReceipts.manifestId, manifest.manifestId), + ); + } catch (error) { + mutationError = error; + } + expect(mutationError).toBeInstanceOf(Error); + expect( + `${String(mutationError)} ${String( + (mutationError as { cause?: unknown }).cause, + )}`, + ).toContain("append-only"); + let deleteError: unknown; + try { + await database() + .delete(schema.syntheticCleanupReceipts) + .where( + eq(schema.syntheticCleanupReceipts.manifestId, manifest.manifestId), + ); + } catch (error) { + deleteError = error; + } + expect( + `${String(deleteError)} ${String( + (deleteError as { cause?: unknown }).cause, + )}`, + ).toContain("append-only"); + + await database() + .insert(schema.syntheticCleanupObjectDeletionAttempts) + .values({ + id: "019fa304-0000-7000-8000-000000000001", + manifestId: manifest.manifestId, + organisationId, + evidenceId: candidateEvidenceId, + versionId: "synthetic-version-1", + authorizationApprovalId: manifest.approvalId, + result: "started", + attemptedByActorId: maintenanceActorId, + traceId: "trace-object-attempt", + }); + let attemptMutationError: unknown; + try { + await database() + .update(schema.syntheticCleanupObjectDeletionAttempts) + .set({ result: "succeeded" }) + .where( + eq( + schema.syntheticCleanupObjectDeletionAttempts.manifestId, + manifest.manifestId, + ), + ); + } catch (error) { + attemptMutationError = error; + } + expect( + `${String(attemptMutationError)} ${String( + (attemptMutationError as { cause?: unknown }).cause, + )}`, + ).toContain("append-only"); + + let provenanceMutationError: unknown; + try { + await database() + .delete(schema.syntheticArtifactProvenance) + .where( + eq(schema.syntheticArtifactProvenance.id, provenance.candidateRoom), + ); + } catch (error) { + provenanceMutationError = error; + } + expect( + `${String(provenanceMutationError)} ${String( + (provenanceMutationError as { cause?: unknown }).cause, + )}`, + ).toContain("append-only"); + }); + + it("requires a fresh independent approval to reconcile a pending object", async () => { + const [receipt] = await database() + .select({ manifest: schema.syntheticCleanupReceipts.manifest }) + .from(schema.syntheticCleanupReceipts) + .where( + eq( + schema.syntheticCleanupReceipts.manifestId, + "019fa301-0000-7000-8000-000000000003", + ), + ); + const manifest = parseSyntheticCleanupManifest(receipt?.manifest); + const retryApprovalId = "019fa302-0000-7000-8000-000000000099"; + const retry = { manifest, retryApprovalId }; + const requested = await requestSyntheticCleanupObjectRetryApproval( + operatorSubject, + retry, + "trace-object-retry-request", + ); + expect(requested).toMatchObject({ + requested: true, + pendingObjectVersions: 1, + }); + await expect( + authoriseSyntheticCleanupObjectRetry( + operatorSubject, + retry, + "trace-object-retry-pending", + ), + ).rejects.toThrow("approval is missing"); + await expect( + recordSyntheticCleanupObjectDeletionAttempt( + operatorSubject, + manifest, + manifest.objectStorageObjects[0]!, + retryApprovalId, + "observed_missing", + "trace-object-retry-unapproved-outcome", + ), + ).rejects.toThrow("approval is not executable"); + await database() + .update(schema.approvals) + .set({ + status: "approved", + decisions: [ + { + actorId: approverActorId, + status: "approved", + reason: "Exact pending immutable object version reviewed", + decidedAt: "2026-07-27T00:03:00.000Z", + }, + ], + decisionAt: new Date("2026-07-27T00:03:00.000Z"), + }) + .where(eq(schema.approvals.id, retryApprovalId)); + const authorised = await authoriseSyntheticCleanupObjectRetry( + operatorSubject, + retry, + "trace-object-retry-authorise", + ); + expect(authorised).toMatchObject({ + authorised: true, + pendingObjects: [{ evidenceId: candidateEvidenceId }], + }); + await recordSyntheticCleanupObjectDeletionAttempt( + operatorSubject, + manifest, + manifest.objectStorageObjects[0]!, + retryApprovalId, + "observed_missing", + "trace-object-retry-observed-missing", + ); + const [attempt, retryJob] = await Promise.all([ + database() + .select({ + authorizationApprovalId: + schema.syntheticCleanupObjectDeletionAttempts + .authorizationApprovalId, + result: schema.syntheticCleanupObjectDeletionAttempts.result, + }) + .from(schema.syntheticCleanupObjectDeletionAttempts) + .where( + and( + eq( + schema.syntheticCleanupObjectDeletionAttempts.manifestId, + manifest.manifestId, + ), + eq( + schema.syntheticCleanupObjectDeletionAttempts.result, + "observed_missing", + ), + ), + ), + database() + .select({ + aggregateType: schema.outboxEvents.aggregateType, + aggregateId: schema.outboxEvents.aggregateId, + queueName: schema.outboxEvents.queueName, + }) + .from(schema.outboxEvents) + .where( + and( + eq(schema.outboxEvents.organisationId, organisationId), + eq( + schema.outboxEvents.idempotencyKey, + `maintenance.synthetic-cleanup:${manifest.manifestId}:object-retry:${retryApprovalId}:queued`, + ), + ), + ), + ]); + expect(attempt[0]).toEqual({ + authorizationApprovalId: retryApprovalId, + result: "observed_missing", + }); + expect(retryJob[0]).toEqual({ + aggregateType: "cleanup_object_retry_approval", + aggregateId: retryApprovalId, + queueName: "muster-maintenance", + }); + await expect( + requestSyntheticCleanupObjectRetryApproval( + operatorSubject, + { + manifest, + retryApprovalId: "019fa302-0000-7000-8000-000000000100", + }, + "trace-object-retry-empty", + ), + ).rejects.toThrow("no pending object versions"); + }); +}); diff --git a/packages/database/src/synthetic-cleanup.test.ts b/packages/database/src/synthetic-cleanup.test.ts new file mode 100644 index 0000000..9e9ada2 --- /dev/null +++ b/packages/database/src/synthetic-cleanup.test.ts @@ -0,0 +1,199 @@ +import { describe, expect, it } from "vitest"; +import { + parseSyntheticCleanupManifest, + protectedDirectMessageIds, + syntheticCleanupManifestDigest, + syntheticCleanupTableDigest, + syntheticCleanupTableKeys, + type SyntheticCleanupManifest, +} from "./synthetic-cleanup.ts"; + +const emptyDigest = syntheticCleanupTableDigest([]); +const provenanceId = "019fa210-0000-7000-8000-000000000099"; +const unsigned: Omit = { + version: 2, + manifestId: "019fa210-0000-7000-8000-000000000001", + approvalId: "019fa210-0000-7000-8000-000000000002", + organisationId: "019fa210-0000-7000-8000-000000000003", + maintenanceActorId: "019fa210-0000-7000-8000-000000000004", + generatedAt: "2026-07-27T00:00:00.000Z", + archiveRoomIds: [], + archiveTaskIds: [], + archiveHuntIds: [], + archiveIntegrationIds: [], + archiveResearchWatchlistIds: [], + archiveReportManifestIds: [], + archiveReportScheduleIds: [], + hideMessageIds: [], + retireEvidenceIds: [], + rejectAgentMemoryIds: [], + retireActorIds: [], + selectionEvidence: [], + objectStorageObjects: [], + tableDigests: Object.fromEntries( + syntheticCleanupTableKeys.map((table) => [table, emptyDigest]), + ) as SyntheticCleanupManifest["tableDigests"], +}; + +function manifest( + overrides: Partial> = {}, +) { + const value = { + ...unsigned, + ...overrides, + } as Omit; + return { ...value, digest: syntheticCleanupManifestDigest(value) }; +} + +describe("synthetic cleanup manifest", () => { + it("accepts exact digest-bound candidates and proof", () => { + const roomId = "019fa210-0000-7000-8000-000000000005"; + const value = manifest({ + archiveRoomIds: [roomId], + selectionEvidence: [ + { + table: "rooms", + recordId: roomId, + provenanceId, + }, + ], + }); + + expect(parseSyntheticCleanupManifest(value)).toMatchObject({ + manifestId: unsigned.manifestId, + archiveRoomIds: [roomId], + }); + }); + + it("rejects tampering", () => { + expect(() => + parseSyntheticCleanupManifest({ + ...manifest(), + generatedAt: "2026-07-27T01:00:00.000Z", + }), + ).toThrow("digest mismatch"); + }); + + it("requires proof for every candidate and no unrelated proof", () => { + const roomId = "019fa210-0000-7000-8000-000000000005"; + expect(() => + parseSyntheticCleanupManifest(manifest({ archiveRoomIds: [roomId] })), + ).toThrow("exactly cover every candidate"); + + expect(() => + parseSyntheticCleanupManifest( + manifest({ + selectionEvidence: [ + { + table: "rooms", + recordId: roomId, + provenanceId, + }, + ], + }), + ), + ).toThrow("exactly cover every candidate"); + }); + + it("preserves genuine direct messages", () => { + const protectedId = protectedDirectMessageIds[0]; + const value = manifest({ + hideMessageIds: [protectedId], + selectionEvidence: [ + { + table: "messages", + recordId: protectedId, + provenanceId, + }, + ], + }); + expect(() => parseSyntheticCleanupManifest(value)).toThrow( + "protected direct message", + ); + }); + + it("cannot retire the authorised maintenance actor", () => { + const value = manifest({ + retireActorIds: [unsigned.maintenanceActorId], + selectionEvidence: [ + { + table: "actors", + recordId: unsigned.maintenanceActorId, + provenanceId, + }, + ], + }); + expect(() => parseSyntheticCleanupManifest(value)).toThrow( + "maintenance actor", + ); + }); + + it("binds object deletion inventory to selected evidence", () => { + const evidenceId = "019fa210-0000-7000-8000-000000000006"; + expect(() => + parseSyntheticCleanupManifest( + manifest({ + objectStorageObjects: [ + { + evidenceId, + bucket: "muster-evidence", + key: "org/evidence.bin", + versionId: "synthetic-version-1", + etag: "synthetic-etag", + size: 1, + sha256: "a".repeat(64), + legalHold: false, + objectLockMetadata: {}, + }, + ], + }), + ), + ).toThrow("selected evidence"); + }); + + it.each(["unversioned", "null"])( + "rejects mutable object version sentinel %s", + (versionId) => { + const evidenceId = "019fa210-0000-7000-8000-000000000006"; + expect(() => + parseSyntheticCleanupManifest( + manifest({ + retireEvidenceIds: [evidenceId], + selectionEvidence: [ + { table: "evidence", recordId: evidenceId, provenanceId }, + ], + objectStorageObjects: [ + { + evidenceId, + bucket: "muster-evidence", + key: "org/evidence.bin", + versionId, + etag: "synthetic-etag", + size: 1, + sha256: "a".repeat(64), + legalHold: false, + objectLockMetadata: {}, + }, + ], + }), + ), + ).toThrow("immutable object version"); + }, + ); + + it("hashes candidate rows independently of query order", () => { + const rows = [ + { + id: "019fa210-0000-7000-8000-000000000008", + updatedAt: new Date("2026-07-27T00:00:00.000Z"), + }, + { + id: "019fa210-0000-7000-8000-000000000007", + updatedAt: new Date("2026-07-26T00:00:00.000Z"), + }, + ]; + expect(syntheticCleanupTableDigest(rows)).toBe( + syntheticCleanupTableDigest([...rows].reverse()), + ); + }); +}); diff --git a/packages/database/src/synthetic-cleanup.ts b/packages/database/src/synthetic-cleanup.ts new file mode 100644 index 0000000..7cd2dfb --- /dev/null +++ b/packages/database/src/synthetic-cleanup.ts @@ -0,0 +1,1994 @@ +import { createHash } from "node:crypto"; +import { + actionApprovalPolicy, + assertExecutableApproval, + ForbiddenError, + requireCapability, + type AuthorisationSubject, +} from "@muster/authz"; +import { and, eq, getTableColumns, inArray, sql } from "drizzle-orm"; +import { z } from "zod"; +import { appendAuditEvent } from "./domain-transaction.ts"; +import { newId } from "./ids.ts"; +import { database } from "./index.ts"; +import { writeOutbox } from "./outbox.ts"; +import * as schema from "./schema.ts"; + +export const protectedDirectMessageIds = [ + "019fa05a-fff0-76ce-9084-bf0707206d15", + "019fa05b-c62c-7368-8166-a23b68e3057f", + "019fa19f-335e-708e-9ce3-be4083921691", + "019fa19f-5c96-7402-8784-0324bb98d48c", +] as const; + +export const syntheticCleanupTableKeys = [ + "rooms", + "tasks", + "hunts", + "integrations", + "researchWatchlists", + "reportManifests", + "reportSchedules", + "messages", + "evidence", + "agentMemories", + "actors", +] as const; + +const SyntheticCleanupTableKeySchema = z.enum(syntheticCleanupTableKeys); +type SyntheticCleanupTableKey = z.infer; + +const ids = z + .array(z.uuid()) + .max(10_000) + .refine( + (value) => new Set(value).size === value.length, + "Candidate IDs must be unique", + ); +const digest = z.string().regex(/^[a-f0-9]{64}$/); +const tableDigests = z + .object({ + rooms: digest, + tasks: digest, + hunts: digest, + integrations: digest, + researchWatchlists: digest, + reportManifests: digest, + reportSchedules: digest, + messages: digest, + evidence: digest, + agentMemories: digest, + actors: digest, + }) + .strict(); +const selectionEvidence = z.object({ + table: SyntheticCleanupTableKeySchema, + recordId: z.uuid(), + provenanceId: z.uuid(), +}); +const objectStorageObject = z + .object({ + evidenceId: z.uuid(), + bucket: z.string().min(1).max(255), + key: z.string().min(1).max(2_048), + versionId: z + .string() + .min(1) + .max(2_048) + .refine( + (value) => value !== "unversioned" && value !== "null", + "Cleanup requires an immutable object version", + ), + etag: z.string().min(1).max(512), + size: z.number().int().nonnegative(), + sha256: digest, + legalHold: z.literal(false), + objectLockMetadata: z.record(z.string(), z.unknown()), + }) + .strict(); + +export const SyntheticCleanupManifestSchema = z + .object({ + version: z.literal(2), + manifestId: z.uuid(), + approvalId: z.uuid(), + organisationId: z.uuid(), + maintenanceActorId: z.uuid(), + generatedAt: z.string().datetime({ offset: true }), + digest, + archiveRoomIds: ids.default([]), + archiveTaskIds: ids.default([]), + archiveHuntIds: ids.default([]), + archiveIntegrationIds: ids.default([]), + archiveResearchWatchlistIds: ids.default([]), + archiveReportManifestIds: ids.default([]), + archiveReportScheduleIds: ids.default([]), + hideMessageIds: ids.default([]), + retireEvidenceIds: ids.default([]), + rejectAgentMemoryIds: ids.default([]), + retireActorIds: ids.default([]), + selectionEvidence: z.array(selectionEvidence).max(110_000), + objectStorageObjects: z.array(objectStorageObject).max(10_000).default([]), + tableDigests, + }) + .strict(); +export const SyntheticCleanupPlanSchema = SyntheticCleanupManifestSchema.omit({ + digest: true, + tableDigests: true, +}); +export const SyntheticCleanupObjectRetrySchema = z + .object({ + manifest: SyntheticCleanupManifestSchema, + retryApprovalId: z.uuid(), + }) + .strict(); + +export type SyntheticCleanupManifest = z.infer< + typeof SyntheticCleanupManifestSchema +>; +export type SyntheticCleanupPlan = z.infer; +export type SyntheticCleanupObject = z.infer; +export type SyntheticCleanupObjectRetry = z.infer< + typeof SyntheticCleanupObjectRetrySchema +>; + +type Database = ReturnType; +type Transaction = Parameters[0]>[0]; +type CandidateRow = { id: string; [key: string]: unknown }; +type CandidateRows = Record; + +function canonical(value: unknown): string { + if (value instanceof Date) return JSON.stringify(value.toISOString()); + if (value === null || typeof value !== "object") return JSON.stringify(value); + if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`; + return `{${Object.entries(value as Record) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([key, entry]) => `${JSON.stringify(key)}:${canonical(entry)}`) + .join(",")}}`; +} + +function sha256(value: string) { + return createHash("sha256").update(value).digest("hex"); +} + +export function syntheticCleanupManifestDigest( + manifest: Omit, +) { + return sha256(canonical(manifest)); +} + +export function syntheticCleanupTableDigest(rows: ReadonlyArray) { + return sha256( + canonical([...rows].sort((left, right) => left.id.localeCompare(right.id))), + ); +} + +function candidateIds( + manifest: SyntheticCleanupManifest, +): Record { + return { + rooms: manifest.archiveRoomIds, + tasks: manifest.archiveTaskIds, + hunts: manifest.archiveHuntIds, + integrations: manifest.archiveIntegrationIds, + researchWatchlists: manifest.archiveResearchWatchlistIds, + reportManifests: manifest.archiveReportManifestIds, + reportSchedules: manifest.archiveReportScheduleIds, + messages: manifest.hideMessageIds, + evidence: manifest.retireEvidenceIds, + agentMemories: manifest.rejectAgentMemoryIds, + actors: manifest.retireActorIds, + }; +} + +function verifySelectionEvidence(manifest: SyntheticCleanupManifest) { + const expected = new Set( + Object.entries(candidateIds(manifest)).flatMap(([table, recordIds]) => + recordIds.map((recordId) => `${table}:${recordId}`), + ), + ); + const actual = new Set( + manifest.selectionEvidence.map((item) => `${item.table}:${item.recordId}`), + ); + if ( + actual.size !== manifest.selectionEvidence.length || + actual.size !== expected.size || + [...expected].some((key) => !actual.has(key)) + ) { + throw new Error( + "Cleanup selection evidence must exactly cover every candidate", + ); + } +} + +export function parseSyntheticCleanupManifest(input: unknown) { + const manifest = SyntheticCleanupManifestSchema.parse(input); + const { digest: manifestDigest, ...unsigned } = manifest; + if (syntheticCleanupManifestDigest(unsigned) !== manifestDigest) { + throw new Error("Cleanup manifest digest mismatch"); + } + validateManifestGuards(manifest); + return manifest; +} + +function validateManifestGuards(manifest: SyntheticCleanupManifest) { + if ( + manifest.hideMessageIds.some((id) => + (protectedDirectMessageIds as readonly string[]).includes(id), + ) + ) { + throw new Error("Cleanup manifest includes protected direct message"); + } + if (manifest.retireActorIds.includes(manifest.maintenanceActorId)) { + throw new Error("Cleanup manifest retires its maintenance actor"); + } + verifySelectionEvidence(manifest); + const retiredEvidence = new Set(manifest.retireEvidenceIds); + if ( + new Set(manifest.objectStorageObjects.map((object) => object.evidenceId)) + .size !== manifest.objectStorageObjects.length || + manifest.objectStorageObjects.length !== retiredEvidence.size || + manifest.objectStorageObjects.some( + (object) => !retiredEvidence.has(object.evidenceId), + ) + ) { + throw new Error( + "Object cleanup inventory must exactly cover selected evidence", + ); + } +} + +function validateAuthenticatedSubject( + subject: AuthorisationSubject, + manifest: SyntheticCleanupManifest, +) { + requireCapability(subject, "administration.manage"); + if ( + subject.organisationId !== manifest.organisationId || + subject.actorId !== manifest.maintenanceActorId + ) { + throw new ForbiddenError("administration.manage"); + } +} + +function assertExactRows( + table: SyntheticCleanupTableKey, + expectedIds: readonly string[], + rows: readonly CandidateRow[], + expectedDigest: string, +) { + const actualIds = rows.map((row) => row.id).sort(); + const sortedExpected = [...expectedIds].sort(); + if ( + actualIds.length !== sortedExpected.length || + actualIds.some((id, index) => id !== sortedExpected[index]) + ) { + throw new Error(`Cleanup ${table} candidate count or ownership changed`); + } + if (syntheticCleanupTableDigest(rows) !== expectedDigest) { + throw new Error(`Cleanup ${table} pre-state digest changed`); + } +} + +async function loadCandidateRows( + tx: Transaction, + manifest: SyntheticCleanupManifest, +): Promise { + const organisationId = manifest.organisationId; + const rooms = manifest.archiveRoomIds.length + ? await tx + .select({ ...getTableColumns(schema.rooms) }) + .from(schema.rooms) + .where( + and( + eq(schema.rooms.organisationId, organisationId), + inArray(schema.rooms.id, manifest.archiveRoomIds), + ), + ) + .for("update") + : []; + const tasks = manifest.archiveTaskIds.length + ? await tx + .select({ ...getTableColumns(schema.tasks) }) + .from(schema.tasks) + .where( + and( + eq(schema.tasks.organisationId, organisationId), + inArray(schema.tasks.id, manifest.archiveTaskIds), + ), + ) + .for("update") + : []; + const hunts = manifest.archiveHuntIds.length + ? await tx + .select({ ...getTableColumns(schema.huntRuns) }) + .from(schema.huntRuns) + .where( + and( + eq(schema.huntRuns.organisationId, organisationId), + inArray(schema.huntRuns.id, manifest.archiveHuntIds), + ), + ) + .for("update") + : []; + const integrations = manifest.archiveIntegrationIds.length + ? await tx + .select({ ...getTableColumns(schema.integrationRecords) }) + .from(schema.integrationRecords) + .where( + and( + eq(schema.integrationRecords.organisationId, organisationId), + inArray( + schema.integrationRecords.id, + manifest.archiveIntegrationIds, + ), + ), + ) + .for("update") + : []; + const researchWatchlists = manifest.archiveResearchWatchlistIds.length + ? await tx + .select({ ...getTableColumns(schema.researchWatchlists) }) + .from(schema.researchWatchlists) + .where( + and( + eq(schema.researchWatchlists.organisationId, organisationId), + inArray( + schema.researchWatchlists.id, + manifest.archiveResearchWatchlistIds, + ), + ), + ) + .for("update") + : []; + const reportManifests = manifest.archiveReportManifestIds.length + ? await tx + .select({ ...getTableColumns(schema.reportManifests) }) + .from(schema.reportManifests) + .where( + and( + eq(schema.reportManifests.organisationId, organisationId), + inArray( + schema.reportManifests.id, + manifest.archiveReportManifestIds, + ), + ), + ) + .for("update") + : []; + const reportSchedules = manifest.archiveReportScheduleIds.length + ? await tx + .select({ ...getTableColumns(schema.reportSchedules) }) + .from(schema.reportSchedules) + .where( + and( + eq(schema.reportSchedules.organisationId, organisationId), + inArray( + schema.reportSchedules.id, + manifest.archiveReportScheduleIds, + ), + ), + ) + .for("update") + : []; + const messages = manifest.hideMessageIds.length + ? await tx + .select({ + ...getTableColumns(schema.messages), + roomType: schema.rooms.roomType, + }) + .from(schema.messages) + .innerJoin( + schema.rooms, + and( + eq(schema.rooms.organisationId, schema.messages.organisationId), + eq(schema.rooms.id, schema.messages.roomId), + ), + ) + .where( + and( + eq(schema.messages.organisationId, organisationId), + inArray(schema.messages.id, manifest.hideMessageIds), + ), + ) + .for("update", { of: [schema.messages, schema.rooms] }) + : []; + const evidence = manifest.retireEvidenceIds.length + ? await tx + .select({ ...getTableColumns(schema.evidence) }) + .from(schema.evidence) + .where( + and( + eq(schema.evidence.organisationId, organisationId), + inArray(schema.evidence.id, manifest.retireEvidenceIds), + ), + ) + .for("update") + : []; + const agentMemories = manifest.rejectAgentMemoryIds.length + ? await tx + .select({ ...getTableColumns(schema.agentMemories) }) + .from(schema.agentMemories) + .where( + and( + eq(schema.agentMemories.organisationId, organisationId), + inArray(schema.agentMemories.id, manifest.rejectAgentMemoryIds), + ), + ) + .for("update") + : []; + const actors = manifest.retireActorIds.length + ? await tx + .select({ ...getTableColumns(schema.actors) }) + .from(schema.actors) + .where( + and( + eq(schema.actors.organisationId, organisationId), + inArray(schema.actors.id, manifest.retireActorIds), + ), + ) + .for("update") + : []; + const rows: CandidateRows = { + rooms, + tasks, + hunts, + integrations, + researchWatchlists, + reportManifests, + reportSchedules, + messages, + evidence, + agentMemories, + actors, + }; + const selectedArtifactIds = Object.values(candidateIds(manifest)).flat(); + const provenance = selectedArtifactIds.length + ? await tx + .select({ + id: schema.syntheticArtifactProvenance.id, + artifactTable: schema.syntheticArtifactProvenance.artifactTable, + artifactId: schema.syntheticArtifactProvenance.artifactId, + sourceKind: schema.syntheticArtifactProvenance.sourceKind, + sourceReference: schema.syntheticArtifactProvenance.sourceReference, + }) + .from(schema.syntheticArtifactProvenance) + .where( + and( + eq( + schema.syntheticArtifactProvenance.organisationId, + organisationId, + ), + inArray( + schema.syntheticArtifactProvenance.artifactId, + selectedArtifactIds, + ), + ), + ) + .for("update") + : []; + const provenanceByArtifact = new Map( + provenance.map((record) => [ + `${record.artifactTable}:${record.artifactId}`, + record, + ]), + ); + for (const table of syntheticCleanupTableKeys) { + for (const row of rows[table]) { + const record = provenanceByArtifact.get(`${table}:${row.id}`); + if (record) { + row.syntheticProvenanceId = record.id; + row.syntheticProvenanceSourceKind = record.sourceKind; + row.syntheticProvenanceSourceReference = record.sourceReference; + } + } + } + if (manifest.retireActorIds.length) { + const protectedMemberships = await tx + .select({ actorId: schema.roomMemberships.actorId }) + .from(schema.roomMemberships) + .innerJoin( + schema.rooms, + and( + eq( + schema.rooms.organisationId, + schema.roomMemberships.organisationId, + ), + eq(schema.rooms.id, schema.roomMemberships.roomId), + ), + ) + .where( + and( + eq(schema.roomMemberships.organisationId, organisationId), + inArray(schema.roomMemberships.actorId, manifest.retireActorIds), + eq(schema.rooms.roomType, "direct"), + ), + ) + .for("update", { of: [schema.roomMemberships, schema.rooms] }); + if (protectedMemberships.length) { + throw new Error("Cleanup cannot retire a direct-room member"); + } + } + return rows; +} + +function verifyCandidateRows( + manifest: SyntheticCleanupManifest, + rows: CandidateRows, +) { + for (const table of syntheticCleanupTableKeys) { + assertExactRows( + table, + candidateIds(manifest)[table], + rows[table], + manifest.tableDigests[table], + ); + } + verifyCandidateStates(manifest, rows); +} + +function verifyCandidateStates( + manifest: SyntheticCleanupManifest, + rows: CandidateRows, +) { + if ( + rows.rooms.some( + (row) => row.archivedAt !== null || row.roomType === "direct", + ) + ) { + throw new Error("Cleanup room is archived or direct"); + } + if ( + rows.tasks.some((row) => row.archivedAt !== null || row.status !== "done") + ) { + throw new Error("Cleanup tasks must be done and unarchived"); + } + if ( + rows.hunts.some( + (row) => + row.archivedAt !== null || + !["completed", "failed", "cancelled"].includes(String(row.status)), + ) + ) { + throw new Error("Cleanup hunts must be terminal and unarchived"); + } + if (rows.integrations.some((row) => row.archivedAt !== null)) { + throw new Error("Cleanup integration is already archived"); + } + if (rows.researchWatchlists.some((row) => row.archivedAt !== null)) { + throw new Error("Cleanup research watchlist is already archived"); + } + if (rows.reportManifests.some((row) => row.archivedAt !== null)) { + throw new Error("Cleanup report manifest is already archived"); + } + if (rows.reportSchedules.some((row) => row.archivedAt !== null)) { + throw new Error("Cleanup report schedule is already archived"); + } + if ( + rows.messages.some( + (row) => row.deletedAt !== null || row.roomType === "direct", + ) + ) { + throw new Error("Cleanup cannot hide deleted or direct-room messages"); + } + if ( + rows.evidence.some( + (row) => row.legalHold === true || row.retentionState === "retired", + ) + ) { + throw new Error("Cleanup evidence is held or already retired"); + } + if (rows.agentMemories.some((row) => row.status === "rejected")) { + throw new Error("Cleanup memory is already rejected"); + } + if ( + rows.actors.some( + (row) => + row.status !== "active" || + row.actorType !== "human" || + row.id === manifest.maintenanceActorId, + ) + ) { + throw new Error("Cleanup actor is protected or already inactive"); + } + const expectedObjects = new Map( + manifest.objectStorageObjects.map((object) => [object.evidenceId, object]), + ); + for (const row of rows.evidence) { + const object = expectedObjects.get(row.id); + if (!object) continue; + if ( + object.key !== row.storageKey || + object.sha256 !== row.sha256 || + object.size !== row.size + ) { + throw new Error("Cleanup object inventory changed"); + } + if ( + object.legalHold || + Object.keys(object.objectLockMetadata).length > 0 || + (row.objectLockMetadata && + Object.keys(row.objectLockMetadata as object).length > 0) + ) { + throw new Error("Cleanup object is locked"); + } + } + const rowsByTable = Object.fromEntries( + syntheticCleanupTableKeys.map((table) => [ + table, + new Map(rows[table].map((row) => [row.id, row])), + ]), + ) as Record>; + for (const evidence of manifest.selectionEvidence) { + const row = rowsByTable[evidence.table].get(evidence.recordId); + if (!row) { + throw new Error("Cleanup selection evidence row is unavailable"); + } + if (row.syntheticProvenanceId !== evidence.provenanceId) { + throw new Error( + "Cleanup candidate lacks exact append-only synthetic provenance", + ); + } + } +} + +async function validateMaintenanceActor( + tx: Transaction, + manifest: SyntheticCleanupManifest, +) { + const [actor] = await tx + .select({ + id: schema.actors.id, + status: schema.actors.status, + actorType: schema.actors.actorType, + capabilities: schema.actors.capabilityAssignments, + }) + .from(schema.actors) + .where( + and( + eq(schema.actors.organisationId, manifest.organisationId), + eq(schema.actors.id, manifest.maintenanceActorId), + ), + ) + .limit(1) + .for("update"); + const capabilities = z.array(z.string()).safeParse(actor?.capabilities); + if ( + !actor || + actor.status !== "active" || + actor.actorType !== "human" || + !capabilities.success || + !capabilities.data.includes("administration.manage") + ) { + throw new Error("Authorised maintenance actor is unavailable"); + } + return actor; +} + +async function validateExecutableApproval( + tx: Transaction, + manifest: SyntheticCleanupManifest, + now: Date, +) { + const [approval] = await tx + .select() + .from(schema.approvals) + .where( + and( + eq(schema.approvals.organisationId, manifest.organisationId), + eq(schema.approvals.id, manifest.approvalId), + ), + ) + .limit(1) + .for("update"); + const target = z + .object({ manifestId: z.uuid(), digest }) + .safeParse(approval?.target); + const decisions = z + .array( + z.object({ + actorId: z.uuid(), + status: z.enum(["approved", "rejected"]), + }), + ) + .safeParse(approval?.decisions); + if ( + !approval || + approval.status !== "approved" || + approval.actionType !== "maintenance.synthetic-cleanup" || + approval.requiredCapability !== "administration.manage" || + approval.requiredApprovalCount !== + actionApprovalPolicy["maintenance.synthetic-cleanup"].approvalCount || + approval.requestingActorId !== manifest.maintenanceActorId || + approval.expiresAt <= now || + approval.executedAt !== null || + !target.success || + target.data.manifestId !== manifest.manifestId || + target.data.digest !== manifest.digest || + !decisions.success + ) { + throw new Error("Executable cleanup approval is missing"); + } + assertExecutableApproval("maintenance.synthetic-cleanup", decisions.data); + const approvedActorIds = [ + ...new Set( + decisions.data + .filter((decision) => decision.status === "approved") + .map((decision) => decision.actorId), + ), + ]; + if (approvedActorIds.length < approval.requiredApprovalCount) { + throw new Error("Cleanup approval count is insufficient"); + } + if (approvedActorIds.includes(manifest.maintenanceActorId)) { + throw new Error("Cleanup requires an independent approver"); + } + const approvers = await tx + .select({ + id: schema.actors.id, + status: schema.actors.status, + capabilities: schema.actors.capabilityAssignments, + }) + .from(schema.actors) + .where( + and( + eq(schema.actors.organisationId, manifest.organisationId), + inArray(schema.actors.id, approvedActorIds), + ), + ) + .for("update"); + if ( + approvers.length !== approvedActorIds.length || + approvers.some((approver) => { + const capabilities = z.array(z.string()).safeParse(approver.capabilities); + return ( + approver.status !== "active" || + !capabilities.success || + !capabilities.data.includes("administration.manage") + ); + }) + ) { + throw new Error("Cleanup approver capability was revoked"); + } + return approval; +} + +function candidateCounts(rows: CandidateRows) { + return Object.fromEntries( + syntheticCleanupTableKeys.map((table) => [table, rows[table].length]), + ) as Record; +} + +async function updateExactly( + expectedIds: readonly string[], + update: () => Promise>, + label: string, +) { + if (!expectedIds.length) return; + const updated = await update(); + if ( + updated.length !== expectedIds.length || + updated.some((row) => !expectedIds.includes(row.id)) + ) { + throw new Error(`Cleanup ${label} affected-count mismatch`); + } +} + +async function applyCandidateTransitions( + tx: Transaction, + manifest: SyntheticCleanupManifest, + now: Date, + idempotencyKey: string, +) { + await updateExactly( + manifest.archiveRoomIds, + () => + tx + .update(schema.rooms) + .set({ archivedAt: now, updatedAt: now }) + .where( + and( + eq(schema.rooms.organisationId, manifest.organisationId), + inArray(schema.rooms.id, manifest.archiveRoomIds), + ), + ) + .returning({ id: schema.rooms.id }), + "rooms", + ); + await updateExactly( + manifest.archiveTaskIds, + () => + tx + .update(schema.tasks) + .set({ archivedAt: now, updatedAt: now }) + .where( + and( + eq(schema.tasks.organisationId, manifest.organisationId), + inArray(schema.tasks.id, manifest.archiveTaskIds), + ), + ) + .returning({ id: schema.tasks.id }), + "tasks", + ); + await updateExactly( + manifest.archiveHuntIds, + () => + tx + .update(schema.huntRuns) + .set({ archivedAt: now, updatedAt: now }) + .where( + and( + eq(schema.huntRuns.organisationId, manifest.organisationId), + inArray(schema.huntRuns.id, manifest.archiveHuntIds), + ), + ) + .returning({ id: schema.huntRuns.id }), + "hunts", + ); + await updateExactly( + manifest.archiveIntegrationIds, + () => + tx + .update(schema.integrationRecords) + .set({ archivedAt: now, status: "disabled", updatedAt: now }) + .where( + and( + eq( + schema.integrationRecords.organisationId, + manifest.organisationId, + ), + inArray( + schema.integrationRecords.id, + manifest.archiveIntegrationIds, + ), + ), + ) + .returning({ id: schema.integrationRecords.id }), + "integrations", + ); + await updateExactly( + manifest.archiveResearchWatchlistIds, + () => + tx + .update(schema.researchWatchlists) + .set({ archivedAt: now, enabled: false, updatedAt: now }) + .where( + and( + eq( + schema.researchWatchlists.organisationId, + manifest.organisationId, + ), + inArray( + schema.researchWatchlists.id, + manifest.archiveResearchWatchlistIds, + ), + ), + ) + .returning({ id: schema.researchWatchlists.id }), + "research watchlists", + ); + await updateExactly( + manifest.archiveReportManifestIds, + () => + tx + .update(schema.reportManifests) + .set({ archivedAt: now, updatedAt: now }) + .where( + and( + eq(schema.reportManifests.organisationId, manifest.organisationId), + inArray( + schema.reportManifests.id, + manifest.archiveReportManifestIds, + ), + ), + ) + .returning({ id: schema.reportManifests.id }), + "report manifests", + ); + await updateExactly( + manifest.archiveReportScheduleIds, + () => + tx + .update(schema.reportSchedules) + .set({ archivedAt: now, enabled: false, updatedAt: now }) + .where( + and( + eq(schema.reportSchedules.organisationId, manifest.organisationId), + inArray( + schema.reportSchedules.id, + manifest.archiveReportScheduleIds, + ), + ), + ) + .returning({ id: schema.reportSchedules.id }), + "report schedules", + ); + for (const messageId of manifest.hideMessageIds) { + const [message] = await tx + .select() + .from(schema.messages) + .where( + and( + eq(schema.messages.organisationId, manifest.organisationId), + eq(schema.messages.id, messageId), + ), + ) + .limit(1); + if (!message || message.deletedAt) { + throw new Error("Cleanup message changed after preflight"); + } + await tx.insert(schema.messageRevisions).values({ + id: newId(), + organisationId: manifest.organisationId, + messageId, + actorId: manifest.maintenanceActorId, + revisionType: "delete", + previousDocument: message.document, + previousPlainText: message.plainText, + nextDocument: null, + nextPlainText: null, + reason: `Synthetic cleanup manifest ${manifest.manifestId}`, + idempotencyKey: `${idempotencyKey}:message:${messageId}`, + }); + const updated = await tx + .update(schema.messages) + .set({ + document: { type: "doc", content: [] }, + plainText: "Message deleted", + deletedAt: now, + }) + .where( + and( + eq(schema.messages.organisationId, manifest.organisationId), + eq(schema.messages.id, messageId), + ), + ) + .returning({ id: schema.messages.id }); + if (updated.length !== 1) { + throw new Error("Cleanup message affected-count mismatch"); + } + } + await updateExactly( + manifest.retireEvidenceIds, + () => + tx + .update(schema.evidence) + .set({ retentionState: "retired" }) + .where( + and( + eq(schema.evidence.organisationId, manifest.organisationId), + inArray(schema.evidence.id, manifest.retireEvidenceIds), + eq(schema.evidence.legalHold, false), + ), + ) + .returning({ id: schema.evidence.id }), + "evidence", + ); + await updateExactly( + manifest.rejectAgentMemoryIds, + () => + tx + .update(schema.agentMemories) + .set({ status: "rejected", expiresAt: now }) + .where( + and( + eq(schema.agentMemories.organisationId, manifest.organisationId), + inArray(schema.agentMemories.id, manifest.rejectAgentMemoryIds), + ), + ) + .returning({ id: schema.agentMemories.id }), + "agent memories", + ); + await updateExactly( + manifest.retireActorIds, + () => + tx + .update(schema.actors) + .set({ status: "inactive" }) + .where( + and( + eq(schema.actors.organisationId, manifest.organisationId), + inArray(schema.actors.id, manifest.retireActorIds), + ), + ) + .returning({ id: schema.actors.id }), + "actors", + ); +} + +function verifyPostCandidateStates( + manifest: SyntheticCleanupManifest, + before: CandidateRows, + after: CandidateRows, + now: Date, +) { + const expected: CandidateRows = Object.fromEntries( + syntheticCleanupTableKeys.map((table) => [ + table, + before[table].map((row) => ({ ...row })), + ]), + ) as CandidateRows; + for (const row of expected.rooms) { + row.archivedAt = now; + row.updatedAt = now; + } + for (const row of expected.tasks) { + row.archivedAt = now; + row.updatedAt = now; + } + for (const row of expected.hunts) { + row.archivedAt = now; + row.updatedAt = now; + } + for (const row of expected.integrations) { + row.archivedAt = now; + row.status = "disabled"; + row.updatedAt = now; + } + for (const row of expected.researchWatchlists) { + row.archivedAt = now; + row.enabled = false; + row.updatedAt = now; + } + for (const row of expected.reportManifests) { + row.archivedAt = now; + row.updatedAt = now; + } + for (const row of expected.reportSchedules) { + row.archivedAt = now; + row.enabled = false; + row.updatedAt = now; + } + for (const row of expected.messages) { + row.document = { type: "doc", content: [] }; + row.plainText = "Message deleted"; + row.deletedAt = now; + } + for (const row of expected.evidence) { + row.retentionState = "retired"; + } + for (const row of expected.agentMemories) { + row.status = "rejected"; + row.expiresAt = now; + } + for (const row of expected.actors) { + row.status = "inactive"; + } + for (const table of syntheticCleanupTableKeys) { + assertExactRows( + table, + candidateIds(manifest)[table], + after[table], + syntheticCleanupTableDigest(expected[table]), + ); + } + return Object.fromEntries( + syntheticCleanupTableKeys.map((table) => [ + table, + syntheticCleanupTableDigest(expected[table]), + ]), + ) as Record; +} + +async function verifyMessageRevisions( + tx: Transaction, + manifest: SyntheticCleanupManifest, + before: CandidateRows, + idempotencyKey: string, +) { + if (!manifest.hideMessageIds.length) return; + const revisionKeys = manifest.hideMessageIds.map( + (messageId) => `${idempotencyKey}:message:${messageId}`, + ); + const revisions = await tx + .select() + .from(schema.messageRevisions) + .where( + and( + eq(schema.messageRevisions.organisationId, manifest.organisationId), + inArray(schema.messageRevisions.idempotencyKey, revisionKeys), + ), + ); + const beforeById = new Map( + before.messages.map((message) => [message.id, message]), + ); + if ( + revisions.length !== manifest.hideMessageIds.length || + revisions.some((revision) => { + const previous = beforeById.get(revision.messageId); + return ( + !previous || + revision.actorId !== manifest.maintenanceActorId || + revision.revisionType !== "delete" || + canonical(revision.previousDocument) !== canonical(previous.document) || + revision.previousPlainText !== previous.plainText || + revision.nextDocument !== null || + revision.nextPlainText !== null || + revision.idempotencyKey !== + `${idempotencyKey}:message:${revision.messageId}` + ); + }) + ) { + throw new Error("Cleanup message revision post-state mismatch"); + } +} + +export async function requestSyntheticCleanupApproval( + subject: AuthorisationSubject, + input: unknown, + traceId: string, + db = database(), +) { + const manifest = parseSyntheticCleanupManifest(input); + validateAuthenticatedSubject(subject, manifest); + return db.transaction( + async (tx) => { + await validateMaintenanceActor(tx, manifest); + const rows = await loadCandidateRows(tx, manifest); + verifyCandidateRows(manifest, rows); + const [prior] = await tx + .select({ + id: schema.approvals.id, + actionType: schema.approvals.actionType, + target: schema.approvals.target, + }) + .from(schema.approvals) + .where( + and( + eq(schema.approvals.organisationId, manifest.organisationId), + eq(schema.approvals.id, manifest.approvalId), + ), + ) + .limit(1) + .for("update"); + if (prior) { + const target = z + .object({ manifestId: z.uuid(), digest }) + .safeParse(prior.target); + if ( + prior.actionType !== "maintenance.synthetic-cleanup" || + !target.success || + target.data.manifestId !== manifest.manifestId || + target.data.digest !== manifest.digest + ) { + throw new Error("Cleanup approval ID is already bound"); + } + return { + requested: false, + manifestId: manifest.manifestId, + approvalId: manifest.approvalId, + candidateCounts: candidateCounts(rows), + }; + } + const policy = actionApprovalPolicy["maintenance.synthetic-cleanup"]; + await tx.insert(schema.approvals).values({ + id: manifest.approvalId, + organisationId: manifest.organisationId, + requestingActorId: manifest.maintenanceActorId, + actionType: "maintenance.synthetic-cleanup", + target: { + manifestId: manifest.manifestId, + digest: manifest.digest, + }, + riskSummary: `Archive or retire ${Object.values(candidateCounts(rows)).reduce((sum, count) => sum + count, 0)} exact synthetic records; object deletion remains post-commit.`, + expiresAt: new Date(Date.now() + 30 * 60_000), + requiredCapability: policy.capability, + requiredApprovalCount: policy.approvalCount, + idempotencyKey: `maintenance.synthetic-cleanup:${manifest.manifestId}:approval`, + }); + await appendAuditEvent(tx, { + organisationId: manifest.organisationId, + actorId: manifest.maintenanceActorId, + actorType: "human", + action: "maintenance.synthetic_cleanup.requested", + targetType: "cleanup_manifest", + targetId: manifest.manifestId, + metadata: { + approvalId: manifest.approvalId, + digest: manifest.digest, + candidateCounts: candidateCounts(rows), + tableDigests: manifest.tableDigests, + }, + traceId, + }); + await writeOutbox(tx, { + organisationId: manifest.organisationId, + eventType: "maintenance.synthetic_cleanup.requested", + aggregateType: "cleanup_manifest", + aggregateId: manifest.manifestId, + queueName: "muster-outbox", + payload: { + manifestId: manifest.manifestId, + approvalId: manifest.approvalId, + digest: manifest.digest, + }, + idempotencyKey: `maintenance.synthetic-cleanup:${manifest.manifestId}:requested`, + traceId, + }); + return { + requested: true, + manifestId: manifest.manifestId, + approvalId: manifest.approvalId, + candidateCounts: candidateCounts(rows), + }; + }, + { isolationLevel: "serializable", accessMode: "read write" }, + ); +} + +export async function captureSyntheticCleanupManifest( + subject: AuthorisationSubject, + input: unknown, + db = database(), +) { + const plan = SyntheticCleanupPlanSchema.parse(input); + const placeholder = { + ...plan, + digest: "0".repeat(64), + tableDigests: Object.fromEntries( + syntheticCleanupTableKeys.map((table) => [table, "0".repeat(64)]), + ), + } as SyntheticCleanupManifest; + validateManifestGuards(placeholder); + validateAuthenticatedSubject(subject, placeholder); + return db.transaction( + async (tx) => { + await validateMaintenanceActor(tx, placeholder); + const rows = await loadCandidateRows(tx, placeholder); + for (const table of syntheticCleanupTableKeys) { + const actualDigest = syntheticCleanupTableDigest(rows[table]); + assertExactRows( + table, + candidateIds(placeholder)[table], + rows[table], + actualDigest, + ); + } + verifyCandidateStates(placeholder, rows); + const capturedTableDigests = Object.fromEntries( + syntheticCleanupTableKeys.map((table) => [ + table, + syntheticCleanupTableDigest(rows[table]), + ]), + ) as SyntheticCleanupManifest["tableDigests"]; + const unsigned: Omit = { + ...plan, + tableDigests: capturedTableDigests, + }; + return { + ...unsigned, + digest: syntheticCleanupManifestDigest(unsigned), + }; + }, + { isolationLevel: "repeatable read", accessMode: "read write" }, + ); +} + +export async function verifySyntheticCleanup( + subject: AuthorisationSubject, + input: unknown, + db = database(), +) { + const manifest = parseSyntheticCleanupManifest(input); + validateAuthenticatedSubject(subject, manifest); + return db.transaction( + async (tx) => { + await validateMaintenanceActor(tx, manifest); + const rows = await loadCandidateRows(tx, manifest); + verifyCandidateRows(manifest, rows); + return { + verified: true, + manifestId: manifest.manifestId, + candidateCounts: candidateCounts(rows), + tableDigests: manifest.tableDigests, + }; + }, + { isolationLevel: "repeatable read", accessMode: "read write" }, + ); +} + +export async function findSyntheticCleanupReceipt( + subject: AuthorisationSubject, + input: unknown, + db = database(), +) { + const manifest = parseSyntheticCleanupManifest(input); + validateAuthenticatedSubject(subject, manifest); + const [receipt] = await db + .select() + .from(schema.syntheticCleanupReceipts) + .where( + and( + eq( + schema.syntheticCleanupReceipts.organisationId, + manifest.organisationId, + ), + eq(schema.syntheticCleanupReceipts.manifestId, manifest.manifestId), + ), + ) + .limit(1); + if (receipt && receipt.manifestDigest !== manifest.digest) { + throw new Error("Completed cleanup manifest digest changed"); + } + return receipt ?? null; +} + +export async function listSyntheticCleanupObjectDeletionAttempts( + subject: AuthorisationSubject, + input: unknown, + db = database(), +) { + const manifest = parseSyntheticCleanupManifest(input); + validateAuthenticatedSubject(subject, manifest); + return db + .select() + .from(schema.syntheticCleanupObjectDeletionAttempts) + .where( + and( + eq( + schema.syntheticCleanupObjectDeletionAttempts.organisationId, + manifest.organisationId, + ), + eq( + schema.syntheticCleanupObjectDeletionAttempts.manifestId, + manifest.manifestId, + ), + ), + ); +} + +function pendingObjectVersions( + manifest: SyntheticCleanupManifest, + attempts: ReadonlyArray<{ + evidenceId: string; + versionId: string; + result: string; + }>, +) { + const completed = new Set( + attempts + .filter( + (attempt) => + attempt.result === "succeeded" || + attempt.result === "observed_missing", + ) + .map((attempt) => `${attempt.evidenceId}:${attempt.versionId}`), + ); + return manifest.objectStorageObjects.filter( + (object) => !completed.has(`${object.evidenceId}:${object.versionId}`), + ); +} + +function objectInventoryDigest(objects: readonly SyntheticCleanupObject[]) { + return sha256( + canonical( + [...objects].sort((left, right) => + left.evidenceId.localeCompare(right.evidenceId), + ), + ), + ); +} + +export async function requestSyntheticCleanupObjectRetryApproval( + subject: AuthorisationSubject, + input: unknown, + traceId: string, + db = database(), +) { + const retry = SyntheticCleanupObjectRetrySchema.parse(input); + const manifest = parseSyntheticCleanupManifest(retry.manifest); + validateAuthenticatedSubject(subject, manifest); + return db.transaction( + async (tx) => { + await validateMaintenanceActor(tx, manifest); + const [receipt] = await tx + .select({ digest: schema.syntheticCleanupReceipts.manifestDigest }) + .from(schema.syntheticCleanupReceipts) + .where( + and( + eq( + schema.syntheticCleanupReceipts.organisationId, + manifest.organisationId, + ), + eq(schema.syntheticCleanupReceipts.manifestId, manifest.manifestId), + ), + ) + .limit(1) + .for("update"); + if (!receipt || receipt.digest !== manifest.digest) { + throw new Error("Cleanup receipt is unavailable for retry"); + } + const attempts = await tx + .select({ + evidenceId: schema.syntheticCleanupObjectDeletionAttempts.evidenceId, + versionId: schema.syntheticCleanupObjectDeletionAttempts.versionId, + result: schema.syntheticCleanupObjectDeletionAttempts.result, + }) + .from(schema.syntheticCleanupObjectDeletionAttempts) + .where( + and( + eq( + schema.syntheticCleanupObjectDeletionAttempts.organisationId, + manifest.organisationId, + ), + eq( + schema.syntheticCleanupObjectDeletionAttempts.manifestId, + manifest.manifestId, + ), + ), + ) + .for("update"); + const pending = pendingObjectVersions(manifest, attempts); + if (!pending.length) { + throw new Error("Cleanup has no pending object versions"); + } + const pendingDigest = objectInventoryDigest(pending); + const [prior] = await tx + .select({ + actionType: schema.approvals.actionType, + target: schema.approvals.target, + }) + .from(schema.approvals) + .where( + and( + eq(schema.approvals.organisationId, manifest.organisationId), + eq(schema.approvals.id, retry.retryApprovalId), + ), + ) + .limit(1) + .for("update"); + if (prior) { + const target = z + .object({ + manifestId: z.uuid(), + manifestDigest: digest, + pendingObjectDigest: digest, + }) + .safeParse(prior.target); + if ( + prior.actionType !== + "maintenance.synthetic-cleanup.object-delete-retry" || + !target.success || + target.data.manifestId !== manifest.manifestId || + target.data.manifestDigest !== manifest.digest || + target.data.pendingObjectDigest !== pendingDigest + ) { + throw new Error("Cleanup object retry approval ID is already bound"); + } + return { + requested: false, + retryApprovalId: retry.retryApprovalId, + pendingObjectVersions: pending.length, + pendingObjectDigest: pendingDigest, + }; + } + const action = "maintenance.synthetic-cleanup.object-delete-retry"; + const policy = actionApprovalPolicy[action]; + await tx.insert(schema.approvals).values({ + id: retry.retryApprovalId, + organisationId: manifest.organisationId, + requestingActorId: manifest.maintenanceActorId, + actionType: action, + target: { + manifestId: manifest.manifestId, + manifestDigest: manifest.digest, + pendingObjectDigest: pendingDigest, + }, + riskSummary: `Retry or reconcile ${pending.length} exact immutable cleanup object versions.`, + expiresAt: new Date(Date.now() + 30 * 60_000), + requiredCapability: policy.capability, + requiredApprovalCount: policy.approvalCount, + idempotencyKey: `maintenance.synthetic-cleanup:${manifest.manifestId}:object-retry:${retry.retryApprovalId}`, + }); + await appendAuditEvent(tx, { + organisationId: manifest.organisationId, + actorId: manifest.maintenanceActorId, + actorType: "human", + action: "maintenance.synthetic_cleanup.object_retry_requested", + targetType: "cleanup_manifest", + targetId: manifest.manifestId, + metadata: { + retryApprovalId: retry.retryApprovalId, + pendingObjectDigest: pendingDigest, + pendingObjectVersions: pending.length, + }, + traceId, + }); + await writeOutbox(tx, { + organisationId: manifest.organisationId, + eventType: "maintenance.synthetic_cleanup.object_retry_requested", + aggregateType: "cleanup_manifest", + aggregateId: manifest.manifestId, + queueName: "muster-outbox", + payload: { + manifestId: manifest.manifestId, + retryApprovalId: retry.retryApprovalId, + pendingObjectDigest: pendingDigest, + }, + idempotencyKey: `maintenance.synthetic-cleanup:${manifest.manifestId}:object-retry:${retry.retryApprovalId}:requested`, + traceId, + }); + return { + requested: true, + retryApprovalId: retry.retryApprovalId, + pendingObjectVersions: pending.length, + pendingObjectDigest: pendingDigest, + }; + }, + { isolationLevel: "serializable", accessMode: "read write" }, + ); +} + +export async function authoriseSyntheticCleanupObjectRetry( + subject: AuthorisationSubject, + input: unknown, + traceId: string, + db = database(), +) { + const retry = SyntheticCleanupObjectRetrySchema.parse(input); + const manifest = parseSyntheticCleanupManifest(retry.manifest); + validateAuthenticatedSubject(subject, manifest); + return db.transaction( + async (tx) => { + await tx.execute( + sql`select pg_advisory_xact_lock(hashtextextended(${retry.retryApprovalId}, 0))`, + ); + await validateMaintenanceActor(tx, manifest); + const [receipt] = await tx + .select({ digest: schema.syntheticCleanupReceipts.manifestDigest }) + .from(schema.syntheticCleanupReceipts) + .where( + and( + eq( + schema.syntheticCleanupReceipts.organisationId, + manifest.organisationId, + ), + eq(schema.syntheticCleanupReceipts.manifestId, manifest.manifestId), + ), + ) + .limit(1) + .for("update"); + if (!receipt || receipt.digest !== manifest.digest) { + throw new Error("Cleanup receipt is unavailable for retry"); + } + const attempts = await tx + .select({ + evidenceId: schema.syntheticCleanupObjectDeletionAttempts.evidenceId, + versionId: schema.syntheticCleanupObjectDeletionAttempts.versionId, + result: schema.syntheticCleanupObjectDeletionAttempts.result, + }) + .from(schema.syntheticCleanupObjectDeletionAttempts) + .where( + and( + eq( + schema.syntheticCleanupObjectDeletionAttempts.organisationId, + manifest.organisationId, + ), + eq( + schema.syntheticCleanupObjectDeletionAttempts.manifestId, + manifest.manifestId, + ), + ), + ) + .for("update"); + const pending = pendingObjectVersions(manifest, attempts); + if (!pending.length) { + throw new Error("Cleanup has no pending object versions"); + } + const pendingDigest = objectInventoryDigest(pending); + const [approval] = await tx + .select() + .from(schema.approvals) + .where( + and( + eq(schema.approvals.organisationId, manifest.organisationId), + eq(schema.approvals.id, retry.retryApprovalId), + ), + ) + .limit(1) + .for("update"); + const target = z + .object({ + manifestId: z.uuid(), + manifestDigest: digest, + pendingObjectDigest: digest, + }) + .safeParse(approval?.target); + const decisions = z + .array( + z.object({ + actorId: z.uuid(), + status: z.enum(["approved", "rejected"]), + }), + ) + .safeParse(approval?.decisions); + const action = "maintenance.synthetic-cleanup.object-delete-retry"; + if ( + !approval || + approval.status !== "approved" || + approval.actionType !== action || + approval.requiredCapability !== "administration.manage" || + approval.requiredApprovalCount !== + actionApprovalPolicy[action].approvalCount || + approval.requestingActorId !== manifest.maintenanceActorId || + approval.expiresAt <= new Date() || + approval.executedAt !== null || + !target.success || + target.data.manifestId !== manifest.manifestId || + target.data.manifestDigest !== manifest.digest || + target.data.pendingObjectDigest !== pendingDigest || + !decisions.success + ) { + throw new Error("Executable cleanup object retry approval is missing"); + } + assertExecutableApproval(action, decisions.data); + const approvedActorIds = [ + ...new Set( + decisions.data + .filter((decision) => decision.status === "approved") + .map((decision) => decision.actorId), + ), + ]; + if (approvedActorIds.includes(manifest.maintenanceActorId)) { + throw new Error( + "Cleanup object retry requires an independent approver", + ); + } + const approvers = await tx + .select({ + id: schema.actors.id, + status: schema.actors.status, + capabilities: schema.actors.capabilityAssignments, + }) + .from(schema.actors) + .where( + and( + eq(schema.actors.organisationId, manifest.organisationId), + inArray(schema.actors.id, approvedActorIds), + ), + ) + .for("update"); + if ( + approvers.length !== approvedActorIds.length || + approvers.some((approver) => { + const capabilities = z + .array(z.string()) + .safeParse(approver.capabilities); + return ( + approver.status !== "active" || + !capabilities.success || + !capabilities.data.includes("administration.manage") + ); + }) + ) { + throw new Error("Cleanup object retry approver capability was revoked"); + } + const [executed] = await tx + .update(schema.approvals) + .set({ status: "executed", executedAt: new Date() }) + .where( + and( + eq(schema.approvals.organisationId, manifest.organisationId), + eq(schema.approvals.id, retry.retryApprovalId), + eq(schema.approvals.status, "approved"), + ), + ) + .returning({ id: schema.approvals.id }); + if (!executed) { + throw new Error("Cleanup object retry approval execution changed"); + } + await appendAuditEvent(tx, { + organisationId: manifest.organisationId, + actorId: manifest.maintenanceActorId, + actorType: "human", + action: "maintenance.synthetic_cleanup.object_retry_authorised", + targetType: "cleanup_manifest", + targetId: manifest.manifestId, + metadata: { + retryApprovalId: retry.retryApprovalId, + pendingObjectDigest: pendingDigest, + pendingObjectVersions: pending.length, + }, + traceId, + }); + await writeOutbox(tx, { + organisationId: manifest.organisationId, + eventType: "maintenance.synthetic_cleanup.object_retry_authorised", + aggregateType: "cleanup_manifest", + aggregateId: manifest.manifestId, + queueName: "muster-outbox", + payload: { + manifestId: manifest.manifestId, + retryApprovalId: retry.retryApprovalId, + pendingObjectDigest: pendingDigest, + }, + idempotencyKey: `maintenance.synthetic-cleanup:${manifest.manifestId}:object-retry:${retry.retryApprovalId}:authorised`, + traceId, + }); + if (pending.length) { + await writeOutbox(tx, { + organisationId: manifest.organisationId, + eventType: "maintenance.synthetic_cleanup.object_delete.queued", + aggregateType: "cleanup_object_retry_approval", + aggregateId: retry.retryApprovalId, + queueName: "muster-maintenance", + payload: { + manifestId: manifest.manifestId, + authorizationApprovalId: retry.retryApprovalId, + }, + idempotencyKey: `maintenance.synthetic-cleanup:${manifest.manifestId}:object-retry:${retry.retryApprovalId}:queued`, + traceId, + }); + } + return { authorised: true, pendingObjects: pending }; + }, + { isolationLevel: "serializable", accessMode: "read write" }, + ); +} + +export async function recordSyntheticCleanupObjectDeletionAttempt( + subject: AuthorisationSubject, + input: unknown, + object: SyntheticCleanupObject, + authorizationApprovalId: string, + result: "started" | "succeeded" | "failed" | "observed_missing", + traceId: string, + errorCode?: string, + db = database(), +) { + const manifest = parseSyntheticCleanupManifest(input); + validateAuthenticatedSubject(subject, manifest); + const expected = manifest.objectStorageObjects.find( + (candidate) => candidate.evidenceId === object.evidenceId, + ); + if (!expected || canonical(expected) !== canonical(object)) { + throw new Error("Cleanup deletion attempt object is not manifest-bound"); + } + return db.transaction(async (tx) => { + const [receipt] = await tx + .select({ + digest: schema.syntheticCleanupReceipts.manifestDigest, + approvalId: schema.syntheticCleanupReceipts.approvalId, + }) + .from(schema.syntheticCleanupReceipts) + .where( + and( + eq( + schema.syntheticCleanupReceipts.organisationId, + manifest.organisationId, + ), + eq(schema.syntheticCleanupReceipts.manifestId, manifest.manifestId), + ), + ) + .limit(1); + if (!receipt || receipt.digest !== manifest.digest) { + throw new Error("Cleanup receipt is unavailable for object deletion"); + } + const [approval] = await tx + .select({ + id: schema.approvals.id, + actionType: schema.approvals.actionType, + requestingActorId: schema.approvals.requestingActorId, + status: schema.approvals.status, + target: schema.approvals.target, + }) + .from(schema.approvals) + .where( + and( + eq(schema.approvals.organisationId, manifest.organisationId), + eq(schema.approvals.id, authorizationApprovalId), + ), + ) + .limit(1); + const originalTarget = z + .object({ manifestId: z.uuid(), digest }) + .safeParse(approval?.target); + const retryTarget = z + .object({ manifestId: z.uuid(), manifestDigest: digest }) + .safeParse(approval?.target); + const originalApproval = + approval?.id === receipt.approvalId && + approval.actionType === "maintenance.synthetic-cleanup" && + originalTarget.success && + originalTarget.data.manifestId === manifest.manifestId && + originalTarget.data.digest === manifest.digest; + const retryApproval = + approval?.actionType === + "maintenance.synthetic-cleanup.object-delete-retry" && + retryTarget.success && + retryTarget.data.manifestId === manifest.manifestId && + retryTarget.data.manifestDigest === manifest.digest; + if ( + !approval || + approval.status !== "executed" || + approval.requestingActorId !== manifest.maintenanceActorId || + (!originalApproval && !retryApproval) + ) { + throw new Error("Cleanup object deletion approval is not executable"); + } + const [attempt] = await tx + .insert(schema.syntheticCleanupObjectDeletionAttempts) + .values({ + id: newId(), + manifestId: manifest.manifestId, + organisationId: manifest.organisationId, + evidenceId: object.evidenceId, + versionId: object.versionId, + authorizationApprovalId, + result, + errorCode: errorCode?.slice(0, 200), + attemptedByActorId: subject.actorId, + traceId, + }) + .returning(); + if (!attempt) throw new Error("Cleanup deletion attempt was not recorded"); + return attempt; + }); +} + +export async function applySyntheticCleanup( + subject: AuthorisationSubject, + input: unknown, + traceId: string, + db = database(), +) { + const manifest = parseSyntheticCleanupManifest(input); + validateAuthenticatedSubject(subject, manifest); + try { + return await db.transaction( + async (tx) => { + await tx.execute( + sql`select pg_advisory_xact_lock(hashtextextended(${manifest.manifestId}, 0))`, + ); + const [prior] = await tx + .select() + .from(schema.syntheticCleanupReceipts) + .where( + and( + eq( + schema.syntheticCleanupReceipts.organisationId, + manifest.organisationId, + ), + eq( + schema.syntheticCleanupReceipts.manifestId, + manifest.manifestId, + ), + ), + ) + .limit(1); + if (prior) { + if (prior.manifestDigest !== manifest.digest) { + throw new Error("Completed cleanup manifest digest changed"); + } + return { + applied: false, + manifestId: manifest.manifestId, + receipt: prior, + }; + } + + const now = new Date(); + await validateMaintenanceActor(tx, manifest); + await validateExecutableApproval(tx, manifest, now); + const rows = await loadCandidateRows(tx, manifest); + verifyCandidateRows(manifest, rows); + const counts = candidateCounts(rows); + const idempotencyKey = `maintenance.synthetic-cleanup:${manifest.manifestId}`; + + await applyCandidateTransitions(tx, manifest, now, idempotencyKey); + const postRows = await loadCandidateRows(tx, manifest); + const postDigests = verifyPostCandidateStates( + manifest, + rows, + postRows, + now, + ); + await verifyMessageRevisions(tx, manifest, rows, idempotencyKey); + const executed = await tx + .update(schema.approvals) + .set({ status: "executed", executedAt: now }) + .where( + and( + eq(schema.approvals.organisationId, manifest.organisationId), + eq(schema.approvals.id, manifest.approvalId), + eq(schema.approvals.status, "approved"), + ), + ) + .returning({ id: schema.approvals.id }); + if (executed.length !== 1) { + throw new Error("Cleanup approval execution changed"); + } + await tx.insert(schema.syntheticCleanupReceipts).values({ + manifestId: manifest.manifestId, + organisationId: manifest.organisationId, + approvalId: manifest.approvalId, + maintenanceActorId: manifest.maintenanceActorId, + manifestDigest: manifest.digest, + manifest, + candidateCounts: counts, + preDigests: manifest.tableDigests, + postDigests, + objectStorageObjects: manifest.objectStorageObjects, + traceId, + appliedAt: now, + }); + await appendAuditEvent(tx, { + organisationId: manifest.organisationId, + actorId: manifest.maintenanceActorId, + actorType: "human", + action: "maintenance.synthetic_cleanup.applied", + targetType: "cleanup_manifest", + targetId: manifest.manifestId, + metadata: { + approvalId: manifest.approvalId, + digest: manifest.digest, + candidateCounts: counts, + preDigests: manifest.tableDigests, + postDigests, + objectStorageObjectCount: manifest.objectStorageObjects.length, + }, + traceId, + }); + await writeOutbox(tx, { + organisationId: manifest.organisationId, + eventType: "maintenance.synthetic_cleanup.applied", + aggregateType: "cleanup_manifest", + aggregateId: manifest.manifestId, + queueName: "muster-outbox", + payload: { + manifestId: manifest.manifestId, + digest: manifest.digest, + candidateCounts: counts, + objectStorageObjects: manifest.objectStorageObjects, + }, + idempotencyKey, + traceId, + }); + if (manifest.objectStorageObjects.length) { + await writeOutbox(tx, { + organisationId: manifest.organisationId, + eventType: "maintenance.synthetic_cleanup.object_delete.queued", + aggregateType: "cleanup_manifest", + aggregateId: manifest.manifestId, + queueName: "muster-maintenance", + payload: { + manifestId: manifest.manifestId, + authorizationApprovalId: manifest.approvalId, + }, + idempotencyKey: `${idempotencyKey}:object-delete:queued`, + traceId, + }); + } + return { + applied: true, + manifestId: manifest.manifestId, + candidateCounts: counts, + preDigests: manifest.tableDigests, + postDigests, + objectStorageObjects: manifest.objectStorageObjects, + }; + }, + { isolationLevel: "serializable", accessMode: "read write" }, + ); + } catch (error) { + const cause = + error && typeof error === "object" && "cause" in error + ? (error as { cause?: unknown }).cause + : undefined; + const code = + cause && typeof cause === "object" && "code" in cause + ? (cause as { code?: unknown }).code + : undefined; + if (code === "40001") { + const receipt = await findSyntheticCleanupReceipt(subject, manifest, db); + if (receipt) { + return { + applied: false, + manifestId: manifest.manifestId, + receipt, + }; + } + } + throw error; + } +} diff --git a/packages/database/src/verify-audit-integrity.ts b/packages/database/src/verify-audit-integrity.ts new file mode 100644 index 0000000..932dc93 --- /dev/null +++ b/packages/database/src/verify-audit-integrity.ts @@ -0,0 +1,58 @@ +import { asc, eq } from "drizzle-orm"; +import { verifyAuditIntegrity } from "@muster/audit"; +import { closeDatabase, database, schema } from "./index.ts"; + +const organisationId = process.env.MUSTER_AUDIT_ORGANISATION_ID; + +if (!organisationId) { + throw new Error("MUSTER_AUDIT_ORGANISATION_ID is required"); +} + +try { + const events = await database() + .select({ + organisationId: schema.auditEvents.organisationId, + sequence: schema.auditEvents.sequence, + actorId: schema.auditEvents.actorId, + actorType: schema.auditEvents.actorType, + action: schema.auditEvents.action, + targetType: schema.auditEvents.targetType, + targetId: schema.auditEvents.targetId, + previousHash: schema.auditEvents.previousHash, + eventHash: schema.auditEvents.eventHash, + metadata: schema.auditEvents.metadata, + traceId: schema.auditEvents.traceId, + createdAt: schema.auditEvents.createdAt, + }) + .from(schema.auditEvents) + .where(eq(schema.auditEvents.organisationId, organisationId)) + .orderBy(asc(schema.auditEvents.sequence)); + + const report = verifyAuditIntegrity( + events.map((event) => ({ + ...event, + createdAt: event.createdAt.toISOString(), + })), + ); + + process.stdout.write( + `${JSON.stringify( + { + organisationId, + eventCount: events.length, + verifiedAt: new Date().toISOString(), + ...report, + }, + null, + 2, + )}\n`, + ); + process.exitCode = + report.outcome === "strict-valid" + ? 0 + : report.outcome === "legacy-compatible-not-strict" + ? 2 + : 1; +} finally { + await closeDatabase(); +} diff --git a/packages/evidence/src/index.ts b/packages/evidence/src/index.ts index fc5b62d..619547b 100644 --- a/packages/evidence/src/index.ts +++ b/packages/evidence/src/index.ts @@ -1,5 +1,7 @@ import { z } from "zod"; +export * from "./object-storage.ts"; + export const evidenceMimeAllowlist = [ "application/json", "application/pdf", @@ -12,20 +14,37 @@ export const evidenceMimeAllowlist = [ export const EvidenceUploadRequestSchema = z.object({ organisationId: z.string().uuid(), - fileName: z.string().min(1).max(255).refine((value) => !/[\/\\\0]/.test(value), "Unsafe file name"), + fileName: z + .string() + .min(1) + .max(255) + .refine((value) => !/[\/\\\0]/.test(value), "Unsafe file name"), mimeType: z.enum(evidenceMimeAllowlist), - size: z.number().int().positive().max(250 * 1024 * 1024), + size: z + .number() + .int() + .positive() + .max(250 * 1024 * 1024), sha256: z.string().regex(/^[a-f0-9]{64}$/), - classification: z.enum(["public","internal","confidential","restricted"]), + classification: z.enum(["public", "internal", "confidential", "restricted"]), }); export interface ObjectStorageAdapter { - presignUpload(storageKey: string, mimeType: string, size: number, expiresSeconds: number): Promise; + presignUpload( + storageKey: string, + mimeType: string, + size: number, + expiresSeconds: number, + ): Promise; presignDownload(storageKey: string, expiresSeconds: number): Promise; quarantine(storageKey: string): Promise; } -export function evidenceStorageKey(organisationId: string, evidenceId: string, fileName: string) { +export function evidenceStorageKey( + organisationId: string, + evidenceId: string, + fileName: string, +) { const safeName = fileName.normalize("NFKC").replace(/[^a-zA-Z0-9._-]/g, "_"); return `organisations/${organisationId}/evidence/${evidenceId}/${safeName}`; } diff --git a/packages/evidence/src/object-storage.test.ts b/packages/evidence/src/object-storage.test.ts new file mode 100644 index 0000000..53b0ebd --- /dev/null +++ b/packages/evidence/src/object-storage.test.ts @@ -0,0 +1,35 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { defaultObjectStorage } from "./object-storage.ts"; + +describe("versioned object storage", () => { + afterEach(() => { + vi.unstubAllEnvs(); + vi.unstubAllGlobals(); + }); + + it("downloads the exact immutable version", async () => { + vi.stubEnv("OBJECT_STORAGE_ENDPOINT", "http://127.0.0.1:9000"); + vi.stubEnv("OBJECT_STORAGE_BUCKET", "muster-evidence"); + const body = new TextEncoder().encode("exact version"); + const fetchMock = vi.fn().mockResolvedValue( + new Response(body, { + status: 200, + headers: { "content-type": "application/octet-stream" }, + }), + ); + vi.stubGlobal("fetch", fetchMock); + + await expect( + defaultObjectStorage.getObjectVersion( + "organisations/test/evidence.bin", + "immutable-version-1", + ), + ).resolves.toEqual(body); + expect(fetchMock).toHaveBeenCalledWith( + expect.objectContaining({ + search: "?versionId=immutable-version-1", + }), + expect.objectContaining({ method: "GET" }), + ); + }); +}); diff --git a/packages/evidence/src/object-storage.ts b/packages/evidence/src/object-storage.ts new file mode 100644 index 0000000..5099725 --- /dev/null +++ b/packages/evidence/src/object-storage.ts @@ -0,0 +1,270 @@ +import { createHash, createHmac } from "node:crypto"; + +export type EvidenceObject = { + storageKey: string; + contentType: string; + body: Uint8Array; +}; + +export interface EvidenceObjectStorage { + putObject(object: EvidenceObject): Promise; +} + +export interface ContentObjectStorage extends EvidenceObjectStorage { + getObject(storageKey: string): Promise; +} + +export interface CleanupObjectStorage extends ContentObjectStorage { + getObjectVersion(storageKey: string, versionId: string): Promise; + headObject( + storageKey: string, + versionId: string, + ): Promise<{ + size: number; + etag: string; + versionId: string; + legalHold: boolean; + objectLockMetadata: Record; + } | null>; + deleteObject(storageKey: string, versionId: string): Promise; +} + +function sha256(value: string | Uint8Array) { + return createHash("sha256").update(value).digest("hex"); +} + +function hmac(key: string | Buffer, value: string) { + return createHmac("sha256", key).update(value).digest(); +} + +function objectUrl(endpoint: string, bucket: string, storageKey: string) { + const url = new URL(endpoint); + const basePath = url.pathname.replace(/\/$/, ""); + const encodedKey = storageKey + .split("/") + .map((part) => encodeURIComponent(part)) + .join("/"); + url.pathname = `${basePath}/${encodeURIComponent(bucket)}/${encodedKey}`; + return url; +} + +function signingHeaders( + method: "DELETE" | "GET" | "HEAD" | "PUT", + url: URL, + body: Uint8Array, + region: string, + accessKey: string, + secretKey: string, + contentType?: string, +) { + const now = new Date(); + const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, ""); + const date = amzDate.slice(0, 8); + const payloadHash = sha256(body); + const canonicalHeaders = [ + ...(contentType ? [`content-type:${contentType}`] : []), + `host:${url.host}`, + `x-amz-content-sha256:${payloadHash}`, + `x-amz-date:${amzDate}`, + ].join("\n"); + const signedHeaders = contentType + ? "content-type;host;x-amz-content-sha256;x-amz-date" + : "host;x-amz-content-sha256;x-amz-date"; + const canonicalRequest = [ + method, + url.pathname, + [...url.searchParams.entries()] + .sort(([left], [right]) => left.localeCompare(right)) + .map( + ([key, value]) => + `${encodeURIComponent(key)}=${encodeURIComponent(value)}`, + ) + .join("&"), + canonicalHeaders, + "", + signedHeaders, + payloadHash, + ].join("\n"); + const scope = `${date}/${region}/s3/aws4_request`; + const stringToSign = [ + "AWS4-HMAC-SHA256", + amzDate, + scope, + sha256(canonicalRequest), + ].join("\n"); + const dateKey = hmac(`AWS4${secretKey}`, date); + const regionKey = hmac(dateKey, region); + const serviceKey = hmac(regionKey, "s3"); + const signingKey = hmac(serviceKey, "aws4_request"); + const signature = createHmac("sha256", signingKey) + .update(stringToSign) + .digest("hex"); + return { + ...(contentType ? { "content-type": contentType } : {}), + "x-amz-content-sha256": payloadHash, + "x-amz-date": amzDate, + authorization: + `AWS4-HMAC-SHA256 Credential=${accessKey}/${scope},` + + ` SignedHeaders=${signedHeaders}, Signature=${signature}`, + }; +} + +function storageConfiguration() { + return { + endpoint: process.env.OBJECT_STORAGE_ENDPOINT ?? "http://127.0.0.1:9000", + bucket: process.env.OBJECT_STORAGE_BUCKET ?? "muster-evidence", + region: process.env.OBJECT_STORAGE_REGION ?? "us-east-1", + accessKey: process.env.OBJECT_STORAGE_ACCESS_KEY ?? "muster", + secretKey: process.env.OBJECT_STORAGE_SECRET_KEY ?? "local-minio-secret", + }; +} + +export const defaultObjectStorage: CleanupObjectStorage = { + async putObject(object) { + const { endpoint, bucket, region, accessKey, secretKey } = + storageConfiguration(); + const url = objectUrl(endpoint, bucket, object.storageKey); + const response = await fetch(url, { + method: "PUT", + headers: signingHeaders( + "PUT", + url, + object.body, + region, + accessKey, + secretKey, + object.contentType, + ), + body: Buffer.from(object.body), + }); + if (!response.ok) { + throw new Error( + `Object storage rejected upload with status ${response.status}`, + ); + } + }, + async getObject(storageKey) { + const { endpoint, bucket, region, accessKey, secretKey } = + storageConfiguration(); + const url = objectUrl(endpoint, bucket, storageKey); + const emptyBody = new Uint8Array(); + const response = await fetch(url, { + method: "GET", + headers: signingHeaders( + "GET", + url, + emptyBody, + region, + accessKey, + secretKey, + ), + }); + if (!response.ok) { + throw new Error( + `Object storage rejected download with status ${response.status}`, + ); + } + return new Uint8Array(await response.arrayBuffer()); + }, + async headObject(storageKey, versionId) { + const { endpoint, bucket, region, accessKey, secretKey } = + storageConfiguration(); + const url = objectUrl(endpoint, bucket, storageKey); + if (versionId !== "unversioned") { + url.searchParams.set("versionId", versionId); + } + const emptyBody = new Uint8Array(); + const response = await fetch(url, { + method: "HEAD", + headers: signingHeaders( + "HEAD", + url, + emptyBody, + region, + accessKey, + secretKey, + ), + }); + if (response.status === 404) return null; + if (!response.ok) { + throw new Error( + `Object storage rejected metadata request with status ${response.status}`, + ); + } + const size = Number(response.headers.get("content-length")); + const etag = response.headers.get("etag")?.replace(/^"|"$/g, ""); + const actualVersionId = + response.headers.get("x-amz-version-id") ?? "unversioned"; + if (!Number.isSafeInteger(size) || size < 0 || !etag || !actualVersionId) { + throw new Error("Object storage returned incomplete version metadata"); + } + const objectLockMetadata = Object.fromEntries( + ["x-amz-object-lock-mode", "x-amz-object-lock-retain-until-date"] + .map((header) => [header, response.headers.get(header)] as const) + .filter((entry): entry is readonly [string, string] => + Boolean(entry[1]), + ), + ); + return { + size, + etag, + versionId: actualVersionId, + legalHold: + response.headers.get("x-amz-object-lock-legal-hold")?.toUpperCase() === + "ON", + objectLockMetadata, + }; + }, + async getObjectVersion(storageKey, versionId) { + const { endpoint, bucket, region, accessKey, secretKey } = + storageConfiguration(); + const url = objectUrl(endpoint, bucket, storageKey); + url.searchParams.set("versionId", versionId); + const emptyBody = new Uint8Array(); + const response = await fetch(url, { + method: "GET", + headers: signingHeaders( + "GET", + url, + emptyBody, + region, + accessKey, + secretKey, + ), + }); + if (!response.ok) { + throw new Error( + `Object storage rejected version download with status ${response.status}`, + ); + } + return new Uint8Array(await response.arrayBuffer()); + }, + async deleteObject(storageKey, versionId) { + const { endpoint, bucket, region, accessKey, secretKey } = + storageConfiguration(); + const url = objectUrl(endpoint, bucket, storageKey); + if (versionId !== "unversioned") { + url.searchParams.set("versionId", versionId); + } + const emptyBody = new Uint8Array(); + const response = await fetch(url, { + method: "DELETE", + headers: signingHeaders( + "DELETE", + url, + emptyBody, + region, + accessKey, + secretKey, + ), + }); + if (!response.ok && response.status !== 404) { + throw new Error( + `Object storage rejected version deletion with status ${response.status}`, + ); + } + }, +}; + +export const defaultEvidenceObjectStorage: EvidenceObjectStorage = + defaultObjectStorage; diff --git a/playwright.config.ts b/playwright.config.ts index 93af907..95f5de8 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -46,21 +46,21 @@ export default defineConfig({ }, { name: "Muster worker", - command: `MUSTER_RESEARCH_TEST_MODE=true CONNECTOR_ENCRYPTION_KEY=0707070707070707070707070707070707070707070707070707070707070707 DATABASE_URL=${testDatabaseUrl} REDIS_URL=${testRedisUrl} AGENT_GATEWAY_URL=http://127.0.0.1:3002 pnpm --dir apps/worker dev`, + command: `MUSTER_RESEARCH_TEST_MODE=true MUSTER_AGENT_GATEWAY_TOKEN=0707070707070707070707070707070707070707070707070707070707070707 CONNECTOR_ENCRYPTION_KEY=0707070707070707070707070707070707070707070707070707070707070707 DATABASE_URL=${testDatabaseUrl} REDIS_URL=${testRedisUrl} AGENT_GATEWAY_URL=http://127.0.0.1:3002 pnpm --dir apps/worker dev`, url: "http://127.0.0.1:3001/ready", reuseExistingServer: !process.env.CI, timeout: 120_000, }, { name: "Muster agent gateway", - command: `MUSTER_AGENT_RUNTIME=mock MUSTER_MOCK_AGENT_DELAY_MS=5000 DATABASE_URL=${testDatabaseUrl} pnpm --dir apps/agent-gateway dev`, + command: `MUSTER_AGENT_RUNTIME=mock MUSTER_AGENT_GATEWAY_TOKEN=0707070707070707070707070707070707070707070707070707070707070707 MUSTER_MOCK_AGENT_DELAY_MS=5000 DATABASE_URL=${testDatabaseUrl} pnpm --dir apps/agent-gateway dev`, url: "http://127.0.0.1:3002/ready", reuseExistingServer: !process.env.CI, timeout: 120_000, }, { name: "Muster web", - command: `MUSTER_RESEARCH_TEST_MODE=true MUSTER_DEMO_MODE=true NEXT_PUBLIC_MUSTER_DEMO_MODE=true CONNECTOR_ENCRYPTION_KEY=0707070707070707070707070707070707070707070707070707070707070707 BETTER_AUTH_SECRET=muster-playwright-secret-at-least-32-characters AUTH_RATE_LIMIT_MAX=10000 DATABASE_URL=${testDatabaseUrl} REDIS_URL=${testRedisUrl} AGENT_GATEWAY_URL=http://127.0.0.1:3002 pnpm --dir apps/web dev`, + command: `MUSTER_RESEARCH_TEST_MODE=true MUSTER_DEMO_MODE=true NEXT_PUBLIC_MUSTER_DEMO_MODE=true MUSTER_AGENT_GATEWAY_TOKEN=0707070707070707070707070707070707070707070707070707070707070707 CONNECTOR_ENCRYPTION_KEY=0707070707070707070707070707070707070707070707070707070707070707 BETTER_AUTH_SECRET=muster-playwright-secret-at-least-32-characters AUTH_RATE_LIMIT_MAX=10000 DATABASE_URL=${testDatabaseUrl} REDIS_URL=${testRedisUrl} AGENT_GATEWAY_URL=http://127.0.0.1:3002 pnpm --dir apps/web dev`, url: "http://127.0.0.1:3000/api/v1/health", reuseExistingServer: !process.env.CI, timeout: 120_000, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d95cb5..d94adf9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -216,6 +216,9 @@ importers: '@muster/database': specifier: workspace:* version: link:../../packages/database + '@muster/evidence': + specifier: workspace:* + version: link:../../packages/evidence '@muster/integrations': specifier: workspace:* version: link:../../packages/integrations @@ -394,6 +397,9 @@ importers: '@muster/audit': specifier: workspace:* version: link:../audit + '@muster/authz': + specifier: workspace:* + version: link:../authz '@muster/contracts': specifier: workspace:* version: link:../contracts diff --git a/scripts/install-homelab.sh b/scripts/install-homelab.sh index 2d75e42..b7f422e 100755 --- a/scripts/install-homelab.sh +++ b/scripts/install-homelab.sh @@ -37,6 +37,7 @@ if [[ ! -f "$env_file" ]]; then auth_secret="$(openssl rand -hex 32)" storage_secret="$(openssl rand -hex 24)" connector_encryption_key="$(openssl rand -hex 32)" + agent_gateway_token="$(openssl rand -hex 32)" admin_password="Muster!$(openssl rand -hex 12)" sed -i.bak \ @@ -47,6 +48,7 @@ if [[ ! -f "$env_file" ]]; then -e "s|generate-better-auth-secret|${auth_secret}|" \ -e "s|generate-object-storage-secret|${storage_secret}|" \ -e "s|generate-connector-encryption-key|${connector_encryption_key}|" \ + -e "s|generate-agent-gateway-token|${agent_gateway_token}|" \ "$env_file" rm "$env_file.bak" { @@ -62,6 +64,12 @@ if ! grep -q '^AUTH_TRUSTED_ORIGINS=' "$env_file"; then "$requested_public_url" "$requested_http_port" >> "$env_file" fi +if ! grep -q '^MUSTER_AGENT_GATEWAY_TOKEN=' "$env_file"; then + printf 'MUSTER_AGENT_GATEWAY_TOKEN=%s\n' "$(openssl rand -hex 32)" \ + >> "$env_file" + chmod 600 "$env_file" +fi + # Persist an explicitly requested reviewed image reference before sourcing the env # file. Otherwise the template's `latest` value would silently win. if [[ -n "$requested_version" ]]; then diff --git a/scripts/test-install-homelab.sh b/scripts/test-install-homelab.sh index 82f6d48..70cd943 100755 --- a/scripts/test-install-homelab.sh +++ b/scripts/test-install-homelab.sh @@ -63,6 +63,8 @@ digest="ghcr.io/jusso-dev/muster@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ./scripts/install-homelab.sh >/dev/null ) grep -Fxq "MUSTER_IMAGE=$digest" "$fixture_root/.env.homelab" +grep -Eq '^MUSTER_AGENT_GATEWAY_TOKEN=[a-f0-9]{64}$' \ + "$fixture_root/.env.homelab" if grep -q '^MUSTER_VERSION=' "$fixture_root/.env.homelab"; then printf 'Digest install retained conflicting MUSTER_VERSION.\n' >&2 exit 1 @@ -70,11 +72,16 @@ fi grep -Fxq "tawny_default" "$DOCKER_NETWORKS" grep -Fxq "kelpie_default" "$DOCKER_NETWORKS" +sed -i.bak -e '/^MUSTER_AGENT_GATEWAY_TOKEN=/d' \ + "$fixture_root/.env.homelab" +rm "$fixture_root/.env.homelab.bak" ( cd "$fixture_root" MUSTER_VERSION=sha-bbbbbbb ./scripts/install-homelab.sh >/dev/null ) grep -Fxq "MUSTER_VERSION=sha-bbbbbbb" "$fixture_root/.env.homelab" +grep -Eq '^MUSTER_AGENT_GATEWAY_TOKEN=[a-f0-9]{64}$' \ + "$fixture_root/.env.homelab" if grep -q '^MUSTER_IMAGE=' "$fixture_root/.env.homelab"; then printf 'Tag transition retained conflicting MUSTER_IMAGE.\n' >&2 exit 1