Adds a new McpToken Prisma model (project-scoped, SHA-256 hashed at rest, optional expiry, revocable) plus backing repository, service, and REST routes. Tokens are a first-class RBAC subject: new 'McpToken' kind is added to the subject enum and the service auto-creates an RbacDefinition with subject McpToken:<sha> when bindings are provided. Creator-permission ceiling: the service rejects any requested binding the creator cannot already satisfy themselves (re-uses rbacService.canAccess / canRunOperation). rbacMode=clone snapshots the creator's full permissions into the token. Routes: POST /api/v1/mcptokens create (returns raw token once) GET /api/v1/mcptokens list (filter by project) GET /api/v1/mcptokens/:id describe (no secret in response) POST /api/v1/mcptokens/:id/revoke soft-delete + remove RbacDef DELETE /api/v1/mcptokens/:id hard-delete GET /api/v1/mcptokens/introspect validate raw bearer (used by mcplocal) Extends AuditEvent with optional tokenName/tokenSha fields (indexed) so token-driven activity can be filtered later. Adds token helpers in @mcpctl/shared: TOKEN_PREFIX='mcpctl_pat_', generateToken, hashToken, isMcpToken, timingSafeEqualHex. Follow-up PRs add the auth-hook dispatch on the prefix, the CLI verbs, and the HTTP-mode mcplocal that calls /introspect. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
369 lines
8.9 KiB
Plaintext
369 lines
8.9 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ── Users ──
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
name String?
|
|
passwordHash String
|
|
role Role @default(USER)
|
|
provider String?
|
|
externalId String?
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
sessions Session[]
|
|
auditLogs AuditLog[]
|
|
ownedProjects Project[]
|
|
groupMemberships GroupMember[]
|
|
mcpTokens McpToken[]
|
|
|
|
@@index([email])
|
|
}
|
|
|
|
enum Role {
|
|
USER
|
|
ADMIN
|
|
}
|
|
|
|
// ── Sessions ──
|
|
|
|
model Session {
|
|
id String @id @default(cuid())
|
|
token String @unique
|
|
userId String
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([token])
|
|
@@index([userId])
|
|
@@index([expiresAt])
|
|
}
|
|
|
|
// ── MCP Servers ──
|
|
|
|
model McpServer {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
description String @default("")
|
|
packageName String?
|
|
runtime String?
|
|
dockerImage String?
|
|
transport Transport @default(STDIO)
|
|
repositoryUrl String?
|
|
externalUrl String?
|
|
command Json?
|
|
containerPort Int?
|
|
replicas Int @default(1)
|
|
env Json @default("[]")
|
|
healthCheck Json?
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
templateName String?
|
|
templateVersion String?
|
|
|
|
instances McpInstance[]
|
|
projects ProjectServer[]
|
|
|
|
@@index([name])
|
|
}
|
|
|
|
enum Transport {
|
|
STDIO
|
|
SSE
|
|
STREAMABLE_HTTP
|
|
}
|
|
|
|
// ── MCP Templates ──
|
|
|
|
model McpTemplate {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
version String @default("1.0.0")
|
|
description String @default("")
|
|
packageName String?
|
|
runtime String?
|
|
dockerImage String?
|
|
transport Transport @default(STDIO)
|
|
repositoryUrl String?
|
|
externalUrl String?
|
|
command Json?
|
|
containerPort Int?
|
|
replicas Int @default(1)
|
|
env Json @default("[]")
|
|
healthCheck Json?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([name])
|
|
}
|
|
|
|
// ── Secrets ──
|
|
|
|
model Secret {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
data Json @default("{}")
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([name])
|
|
}
|
|
|
|
// ── Groups ──
|
|
|
|
model Group {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
description String @default("")
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
members GroupMember[]
|
|
|
|
@@index([name])
|
|
}
|
|
|
|
model GroupMember {
|
|
id String @id @default(cuid())
|
|
groupId String
|
|
userId String
|
|
createdAt DateTime @default(now())
|
|
|
|
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([groupId, userId])
|
|
@@index([groupId])
|
|
@@index([userId])
|
|
}
|
|
|
|
// ── RBAC Definitions ──
|
|
|
|
model RbacDefinition {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
subjects Json @default("[]")
|
|
roleBindings Json @default("[]")
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([name])
|
|
}
|
|
|
|
// ── Projects ──
|
|
|
|
model Project {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
description String @default("")
|
|
prompt String @default("")
|
|
proxyModel String @default("")
|
|
gated Boolean @default(true)
|
|
llmProvider String?
|
|
llmModel String?
|
|
serverOverrides Json?
|
|
ownerId String
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
|
servers ProjectServer[]
|
|
prompts Prompt[]
|
|
promptRequests PromptRequest[]
|
|
mcpTokens McpToken[]
|
|
|
|
@@index([name])
|
|
@@index([ownerId])
|
|
}
|
|
|
|
model ProjectServer {
|
|
id String @id @default(cuid())
|
|
projectId String
|
|
serverId String
|
|
createdAt DateTime @default(now())
|
|
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
server McpServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([projectId, serverId])
|
|
}
|
|
|
|
// ── MCP Tokens (bearer credentials for HTTP-mode mcplocal) ──
|
|
//
|
|
// Raw value format: `mcpctl_pat_<32 base62 chars>`. The raw value is shown
|
|
// exactly once at create time; only the SHA-256 hash is persisted. Tokens are
|
|
// scoped to exactly one project — they're only valid at
|
|
// `/projects/<that-project>/mcp`. Creator's RBAC is the ceiling; the service
|
|
// rejects bindings that exceed what the creator themselves can do.
|
|
|
|
model McpToken {
|
|
id String @id @default(cuid())
|
|
name String
|
|
projectId String
|
|
tokenHash String @unique
|
|
tokenPrefix String
|
|
ownerId String
|
|
description String @default("")
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime?
|
|
lastUsedAt DateTime?
|
|
revokedAt DateTime?
|
|
|
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([name, projectId])
|
|
@@index([tokenHash])
|
|
@@index([projectId])
|
|
@@index([ownerId])
|
|
}
|
|
|
|
// ── MCP Instances (running containers) ──
|
|
|
|
model McpInstance {
|
|
id String @id @default(cuid())
|
|
serverId String
|
|
containerId String?
|
|
status InstanceStatus @default(STOPPED)
|
|
port Int?
|
|
metadata Json @default("{}")
|
|
healthStatus String?
|
|
lastHealthCheck DateTime?
|
|
events Json @default("[]")
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
server McpServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([serverId])
|
|
@@index([status])
|
|
}
|
|
|
|
enum InstanceStatus {
|
|
STARTING
|
|
RUNNING
|
|
STOPPING
|
|
STOPPED
|
|
ERROR
|
|
}
|
|
|
|
// ── Prompts (approved content resources) ──
|
|
|
|
model Prompt {
|
|
id String @id @default(cuid())
|
|
name String
|
|
content String @db.Text
|
|
projectId String?
|
|
priority Int @default(5)
|
|
summary String? @db.Text
|
|
chapters Json?
|
|
linkTarget String?
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([name, projectId])
|
|
@@index([projectId])
|
|
}
|
|
|
|
// ── Prompt Requests (pending proposals from LLM sessions) ──
|
|
|
|
model PromptRequest {
|
|
id String @id @default(cuid())
|
|
name String
|
|
content String @db.Text
|
|
projectId String?
|
|
priority Int @default(5)
|
|
createdBySession String?
|
|
createdByUserId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([name, projectId])
|
|
@@index([projectId])
|
|
@@index([createdBySession])
|
|
}
|
|
|
|
// ── Audit Events (pipeline/gate/tool trace from mcplocal) ──
|
|
|
|
model AuditEvent {
|
|
id String @id @default(cuid())
|
|
timestamp DateTime
|
|
sessionId String
|
|
projectName String
|
|
eventKind String
|
|
source String
|
|
verified Boolean @default(false)
|
|
serverName String?
|
|
correlationId String?
|
|
parentEventId String?
|
|
userName String?
|
|
tokenName String?
|
|
tokenSha String?
|
|
payload Json
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([sessionId])
|
|
@@index([projectName])
|
|
@@index([correlationId])
|
|
@@index([timestamp])
|
|
@@index([eventKind])
|
|
@@index([userName])
|
|
@@index([tokenSha])
|
|
}
|
|
|
|
// ── Backup Pending Queue ──
|
|
|
|
model BackupPending {
|
|
id String @id @default(cuid())
|
|
resourceKind String
|
|
resourceName String
|
|
action String // 'create' | 'update' | 'delete'
|
|
userName String
|
|
yamlContent String? @db.Text
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([createdAt])
|
|
}
|
|
|
|
// ── Audit Logs ──
|
|
|
|
model AuditLog {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
action String
|
|
resource String
|
|
resourceId String?
|
|
details Json @default("{}")
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([action])
|
|
@@index([resource])
|
|
@@index([createdAt])
|
|
}
|