// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" previewFeatures = ["metrics", "relationJoins"] } generator json { provider = "prisma-json-types-generator" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // // Account // model Account { id String @id @default(cuid()) publicKey String @unique seq Int @default(0) feedSeq BigInt @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt settings String? settingsVersion Int @default(0) githubUserId String? @unique githubUser GithubUser? @relation(fields: [githubUserId], references: [id]) // Profile firstName String? lastName String? username String? @unique /// [ImageRef] avatar Json? Session Session[] AccountPushToken AccountPushToken[] TerminalAuthRequest TerminalAuthRequest[] AccountAuthRequest AccountAuthRequest[] UsageReport UsageReport[] Machine Machine[] UploadedFile UploadedFile[] ServiceAccountToken ServiceAccountToken[] RelationshipsFrom UserRelationship[] @relation("RelationshipsFrom") RelationshipsTo UserRelationship[] @relation("RelationshipsTo") Artifact Artifact[] AccessKey AccessKey[] UserFeedItem UserFeedItem[] UserKVStore UserKVStore[] } model TerminalAuthRequest { id String @id @default(cuid()) publicKey String @unique supportsV2 Boolean @default(false) response String? responseAccountId String? responseAccount Account? @relation(fields: [responseAccountId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model AccountAuthRequest { id String @id @default(cuid()) publicKey String @unique response String? responseAccountId String? responseAccount Account? @relation(fields: [responseAccountId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model AccountPushToken { id String @id @default(cuid()) accountId String account Account @relation(fields: [accountId], references: [id]) token String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([accountId, token]) } // // Sessions // model Session { id String @id @default(cuid()) tag String accountId String account Account @relation(fields: [accountId], references: [id]) metadata String metadataVersion Int @default(0) agentState String? agentStateVersion Int @default(0) dataEncryptionKey Bytes? seq Int @default(0) active Boolean @default(true) lastActiveAt DateTime @default(now()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt messages SessionMessage[] usageReports UsageReport[] accessKeys AccessKey[] @@unique([accountId, tag]) @@index([accountId, updatedAt(sort: Desc)]) } model SessionMessage { id String @id @default(cuid()) sessionId String session Session @relation(fields: [sessionId], references: [id]) localId String? seq Int /// [SessionMessageContent] content Json createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([sessionId, localId]) @@index([sessionId, seq]) } // // Github // model GithubUser { id String @id /// [GitHubProfile] profile Json token Bytes? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt Account Account[] } model GithubOrganization { id String @id /// [GitHubOrg] profile Json createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } // // Utility // model GlobalLock { key String @id @default(cuid()) value String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt expiresAt DateTime } model RepeatKey { key String @id value String createdAt DateTime @default(now()) expiresAt DateTime } model SimpleCache { key String @id value String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } // // Usage Reporting // model UsageReport { id String @id @default(cuid()) key String accountId String account Account @relation(fields: [accountId], references: [id]) sessionId String? session Session? @relation(fields: [sessionId], references: [id]) /// [UsageReportData] data Json createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([accountId, sessionId, key]) @@index([accountId]) @@index([sessionId]) } // // Machines // model Machine { id String @id accountId String account Account @relation(fields: [accountId], references: [id]) metadata String // Encrypted - contains static machine info metadataVersion Int @default(0) daemonState String? // Encrypted - contains dynamic daemon state daemonStateVersion Int @default(0) dataEncryptionKey Bytes? seq Int @default(0) active Boolean @default(true) lastActiveAt DateTime @default(now()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accessKeys AccessKey[] @@unique([accountId, id]) @@index([accountId]) } model UploadedFile { id String @id @default(cuid()) accountId String account Account @relation(fields: [accountId], references: [id]) path String width Int? height Int? thumbhash String? reuseKey String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([accountId, path]) @@index([accountId]) } model ServiceAccountToken { id String @id @default(cuid()) accountId String account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) vendor String token Bytes // Encrypted token metadata Json? // Optional vendor metadata lastUsedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([accountId, vendor]) @@index([accountId]) } // // Artifacts // model Artifact { id String @id // UUID provided by client accountId String account Account @relation(fields: [accountId], references: [id]) header Bytes // Encrypted header (can contain JSON) headerVersion Int @default(0) body Bytes // Encrypted body bodyVersion Int @default(0) dataEncryptionKey Bytes // Encryption key for this artifact seq Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([accountId]) @@index([accountId, updatedAt(sort: Desc)]) } // // Access Keys // model AccessKey { id String @id @default(cuid()) accountId String account Account @relation(fields: [accountId], references: [id]) machineId String machine Machine @relation(fields: [accountId, machineId], references: [accountId, id]) sessionId String session Session @relation(fields: [sessionId], references: [id]) data String // Encrypted data dataVersion Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([accountId, machineId, sessionId]) @@index([accountId]) @@index([sessionId]) @@index([machineId]) } // // Social Network - Relationships // enum RelationshipStatus { none requested pending friend rejected } model UserRelationship { fromUserId String fromUser Account @relation("RelationshipsFrom", fields: [fromUserId], references: [id], onDelete: Cascade) toUserId String toUser Account @relation("RelationshipsTo", fields: [toUserId], references: [id], onDelete: Cascade) status RelationshipStatus @default(pending) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt acceptedAt DateTime? lastNotifiedAt DateTime? @@id([fromUserId, toUserId]) @@index([toUserId, status]) @@index([fromUserId, status]) } // // Feed // model UserFeedItem { id String @id @default(cuid()) userId String user Account @relation(fields: [userId], references: [id], onDelete: Cascade) counter BigInt repeatKey String? /// [FeedBody] body Json createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([userId, counter]) @@unique([userId, repeatKey]) @@index([userId, counter(sort: Desc)]) } // // Key-Value Storage // model UserKVStore { id String @id @default(cuid()) accountId String account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) key String // Unencrypted for indexing value Bytes? // Encrypted value, null when "deleted" version Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([accountId, key]) @@index([accountId]) }