merge: integrate v7-rbac-visibility on top of skills feature

Brings together the two unmerged feature lines onto one integration branch:
- skills/review/proposals/revisions (via fix/mcpd-instance-health-and-retry,
  which contains the full skills-1..7 chain + mcpd env/retry/readiness fix)
- v7 visibility scope + ownership for Llms and Agents

Auto-merge resolved schema.prisma, mcpd main.ts, mcplocal config, CLI create,
and completions with no conflicts. Both Prisma migrations coexist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michal
2026-06-16 21:31:35 +01:00
24 changed files with 828 additions and 45 deletions

View File

@@ -225,6 +225,30 @@ model Llm {
lastHeartbeatAt DateTime? // bumped on every publisher heartbeat
status LlmStatus @default(active)
inactiveSince DateTime? // when status flipped from active; used for 4-h GC
// ── Per-user RBAC scoping (v7) ──
// `ownerId` records who created/published the row. NULL on legacy rows
// (those created before the v7 migration) — those continue to behave
// as `visibility=public` for back-compat. New rows always carry an
// ownerId set by the service layer (`User.id` of the authenticated
// caller, or the publishing mcplocal user for virtuals).
//
// `visibility` controls who can see / use the row:
// - 'public' : anyone with the resource grant (`view:llms`,
// `run:llms:<name>`, etc.) sees it. Legacy default
// mirrors today's behavior — explicit
// `mcpctl create llm` calls keep this default.
// - 'private' : only the owner sees it by default; other users need
// an explicit name-scoped RBAC binding
// (`view:llms:<name>`, `run:llms:<name>`). The list
// endpoint hides foreign-private rows from
// unauthorized callers; get/describe returns 404 to
// prevent name enumeration.
//
// mcplocal-published virtual Llms default to 'private' on register —
// a workstation-published model isn't typically meant for the whole
// org until the publisher explicitly shares it. See docs/virtual-llms.md.
ownerId String?
visibility String @default("public")
version Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -238,6 +262,10 @@ model Llm {
@@index([kind, status])
@@index([providerSessionId])
@@index([poolName])
// List filter on the hot path: "rows visible to caller X" decomposes
// into `visibility='public' OR ownerId=X` + an RBAC join. Composite
// index keeps the predicate fast even on a large table.
@@index([visibility, ownerId])
}
// ── Groups ──
@@ -641,6 +669,14 @@ model Agent {
status LlmStatus @default(active)
inactiveSince DateTime?
ownerId String
// v7: per-user RBAC scoping. Mirrors `Llm.visibility` semantics —
// 'public' (default, today's behavior) lets anyone with the resource
// grant see the agent; 'private' restricts to owner + explicit
// name-scoped RBAC bindings. mcplocal-published virtual agents
// default to 'private' on register so a workstation-published persona
// isn't broadcast to the whole org until shared explicitly. Existing
// rows backfill to 'public' so pre-v7 setups keep working unchanged.
visibility String @default("public")
version Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -662,6 +698,7 @@ model Agent {
@@index([defaultPersonalityId])
@@index([kind, status])
@@index([providerSessionId])
@@index([visibility, ownerId])
}
// ── Personalities (named overlay bundles of prompts on top of an Agent) ──