Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,168 @@
|
||||
// Phase 06 #21 — Plugin runtime metadata.
|
||||
//
|
||||
// One row per installed plugin per tenant. The .wasm binary lives on
|
||||
// disk (managed by the operator and verified via wasmSha256); this
|
||||
// table only stores the metadata the worker needs to look up + verify
|
||||
// + invoke a plugin: which version is installed, who signed it, what
|
||||
// capabilities the manifest declared, and whether it is currently
|
||||
// enabled.
|
||||
//
|
||||
// Hot-reload and side-by-side version updates are intentionally
|
||||
// deferred. Per-tenant capability grants are modeled by
|
||||
// TenantPluginCapabilityGrant below, and hook subscriptions are declared in
|
||||
// the signed plugin manifest.
|
||||
|
||||
model Plugin {
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
pluginId String
|
||||
version String
|
||||
manifest Json
|
||||
/// Hex SHA-256 of the .wasm binary at install time. Worker re-hashes
|
||||
/// on load and refuses to invoke when this does not match.
|
||||
wasmSha256 String
|
||||
/// Ed25519 public key (hex) recorded from the manifest at install.
|
||||
signaturePublicKey String
|
||||
enabled Boolean @default(false)
|
||||
installedAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
userConfigs PluginUserConfig[]
|
||||
capabilityGrants TenantPluginCapabilityGrant[]
|
||||
|
||||
@@unique([tenantId, pluginId])
|
||||
@@index([tenantId, enabled])
|
||||
@@index([pluginId, version])
|
||||
}
|
||||
|
||||
// Phase 06 / F-001 — explicit per-tenant capability consent.
|
||||
//
|
||||
// Phase 22 originally deferred the consent dialog and let every
|
||||
// manifest-declared capability run implicitly. F-001 closes that gap:
|
||||
// installs no longer auto-enable; the lifecycle service requires a
|
||||
// matching `TenantPluginCapabilityGrant` row for every capability the
|
||||
// manifest declares before the Plugin row can transition to
|
||||
// `enabled = true`. The UI surface that drives the operator-facing
|
||||
// dialog is intentionally NOT shipped in this commit (separate task);
|
||||
// the backend stays usable from CLI / API today.
|
||||
//
|
||||
// One row per (tenantId, pluginId, capabilityName). `scopeJson` carries
|
||||
// the per-capability scope the operator approved (e.g. allowed-hosts
|
||||
// list for http.outbound) — when null, the manifest's declared scope
|
||||
// applies as-is.
|
||||
model TenantPluginCapabilityGrant {
|
||||
id String @id @default(cuid())
|
||||
tenantId String
|
||||
pluginId String
|
||||
capabilityName String
|
||||
scopeJson Json?
|
||||
grantedAt DateTime @default(now())
|
||||
/// User id of the operator who approved the grant; null for installs
|
||||
/// approved via the CLI / automation path.
|
||||
grantedByUserId String?
|
||||
|
||||
plugin Plugin @relation(fields: [pluginId, tenantId], references: [pluginId, tenantId], onDelete: Cascade)
|
||||
|
||||
@@unique([tenantId, pluginId, capabilityName])
|
||||
@@index([tenantId, pluginId])
|
||||
}
|
||||
|
||||
// Phase 06 #22 — per-user plugin enablement + config blob.
|
||||
//
|
||||
// Default: an installed + enabled Plugin row applies to every user in
|
||||
// the tenant. When a plugin needs per-user opt-in (e.g. a Slack
|
||||
// notifier that asks the user for their own channel) the host writes
|
||||
// a PluginUserConfig row and the hook producer respects `enabled`
|
||||
// before fanning out the invocation.
|
||||
//
|
||||
// `configJson` is a free-form per-user settings blob (Slack channel,
|
||||
// preferred locale, etc.). Plugins read it through the plugin sandbox
|
||||
// host-binding once that capability lands; for the foundation only the
|
||||
// host UI writes it.
|
||||
model PluginUserConfig {
|
||||
id String @id @default(cuid())
|
||||
pluginId String
|
||||
userId String
|
||||
enabled Boolean @default(true)
|
||||
configJson Json?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
plugin Plugin @relation(fields: [pluginId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([pluginId, userId])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
// Phase 06 #23 — durable per-invocation audit row.
|
||||
//
|
||||
// One row per `toolctrl.plugin-invoke` job outcome. Capability-use rows
|
||||
// reference the same `pluginInvocationId` so operators can correlate the
|
||||
// high-level invocation outcome with the individual host capability
|
||||
// calls emitted during sandbox execution. The row intentionally stores
|
||||
// outcome metadata only, not raw plugin output or input payloads.
|
||||
// No FK is declared yet so pre-existing capability-use rows and retry
|
||||
// batches remain valid; correlate records by this stable invocation id.
|
||||
model PluginInvocation {
|
||||
id String @id
|
||||
tenantId String
|
||||
pluginId String
|
||||
version String
|
||||
hookName String
|
||||
outcome String
|
||||
failureReason String?
|
||||
message String?
|
||||
durationMs Int
|
||||
fuelUsed Int?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([tenantId, createdAt(sort: Desc)])
|
||||
@@index([tenantId, pluginId, createdAt(sort: Desc)])
|
||||
@@index([outcome, createdAt(sort: Desc)])
|
||||
}
|
||||
|
||||
// Phase 06 #23 — per-capability-use audit row.
|
||||
//
|
||||
// Every successful (or denied / errored) host-fn call inside a plugin
|
||||
// invocation appends a row here so a tenant admin can answer "what did
|
||||
// this plugin actually DO?" — independent of what the manifest said it
|
||||
// might do. The data is intentionally minimal: capability name, a
|
||||
// hashed pointer to the payload (no raw body — DSGVO + storage bloat),
|
||||
// outcome, and a timestamp. Retention is governed by
|
||||
// `PLUGIN_AUDIT_RETENTION_DAYS` (cron-cleaned by the worker).
|
||||
//
|
||||
// Rows are batch-written from the capability gateway via the outbox
|
||||
// pattern — the gateway buffers per-invocation events and emits one
|
||||
// `plugin.capability-use.batch` outbox event at sandbox close so we
|
||||
// never block the plugin-hot-path on the DB.
|
||||
model PluginCapabilityUse {
|
||||
id String @id @default(cuid())
|
||||
/// PluginInvocation row id. No FK yet; rows are correlated by value so
|
||||
/// existing capability-use batches remain migration-safe.
|
||||
pluginInvocationId String
|
||||
tenantId String
|
||||
pluginId String
|
||||
capability String
|
||||
/// SHA-256 of the host-fn arg-shape (URL+method for http.outbound,
|
||||
/// key for storage.*, etc.) — lets forensics cross-check without
|
||||
/// retaining tenant payload bodies.
|
||||
payloadHash String?
|
||||
outcome String
|
||||
errorCode String?
|
||||
durationMs Int?
|
||||
timestamp DateTime @default(now())
|
||||
|
||||
/// F-003 — per-tenant hash chain. `prevHash` is the `rowHash` of the
|
||||
/// preceding row in this tenant's chain (or NULL for the genesis
|
||||
/// row). `rowHash` is SHA-256(prevHash || canonical(row)). A verifier
|
||||
/// can walk the chain and detect tampering: any modified or deleted
|
||||
/// row breaks the chain at that point. Nullable for back-compat with
|
||||
/// pre-F-003 rows; new inserts always populate both fields.
|
||||
prevHash String?
|
||||
rowHash String?
|
||||
|
||||
@@index([tenantId, timestamp(sort: Desc)])
|
||||
@@index([tenantId, pluginId, timestamp(sort: Desc)])
|
||||
@@index([pluginInvocationId])
|
||||
@@index([timestamp], name: "plugin_capability_use_timestamp_idx")
|
||||
}
|
||||
Reference in new issue
Block a user