{"record":{"id":"05c95d0efdb95bf8","repo":"JuliusBrussee/caveman","slug":"cave-eve-runtime-identity-missing","errorCode":"cave_eve_runtime_identity_missing","errorMessage":"cave_eve_runtime_identity_missing","messagePattern":"cave_eve_runtime_identity_missing","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/agent/src/adapters.ts","lineNumber":480,"sourceCode":"    totalTokens: strictInteger(value.totalTokens, error),\n  };\n  if (usage.totalTokens !== usage.inputTokens + usage.outputTokens +\n      usage.cacheReadTokens + usage.cacheWriteTokens ||\n      usage.reasoningTokens > usage.outputTokens) {\n    throw new Error(error);\n  }\n  return usage;\n}\n\nfunction eveRuntimeIdentity(events: unknown[]): {\n  provider: string;\n  model: string;\n  upstreamVersion: string;\n} {\n  const starts = events.filter((event) =>\n    isRecord(event) && event.type === \"session.started\" && isRecord(event.data) &&\n    isRecord(event.data.runtime));\n  if (starts.length !== 1) throw new Error(\"cave_eve_runtime_identity_missing\");\n  const runtime = (starts[0] as { data: { runtime: Record<string, unknown> } }).data.runtime;\n  if (typeof runtime.modelId !== \"string\" || runtime.modelId.length === 0 ||\n      runtime.modelId.startsWith(\"dynamic:\")) {\n    throw new Error(\"cave_eve_runtime_identity_missing\");\n  }\n  const separator = runtime.modelId.indexOf(\"/\");\n  if (separator < 1 || separator === runtime.modelId.length - 1 ||\n      typeof runtime.eveVersion !== \"string\" || runtime.eveVersion.length === 0) {\n    throw new Error(\"cave_eve_runtime_identity_missing\");\n  }\n  return {\n    provider: runtime.modelId.slice(0, separator),\n    model: runtime.modelId.slice(separator + 1),\n    upstreamVersion: runtime.eveVersion,\n  };\n}\n\nfunction expectedProviderModel(plan: CavePlan): { provider: string; model: string } {","sourceCodeStart":462,"sourceCodeEnd":498,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/adapters.ts#L462-L498","documentation":"Returned by ValidateTenantSchema when tenantSchemaViolations finds at least one breach of the structural isolation invariants: a tenant table without FORCE RLS, a missing or wrong-organization_id RLS policy expression (must equal current_setting('app.current_organization_id', ...) with optional casts), a nullable organization_id where it must not be, or a tenant-to-tenant foreign key that does not share the organization scope (plus project scope when both sides have it). Violations are joined with '; ' into one message. This is a security invariant failure: the schema as deployed permits cross-tenant access.","triggerScenarios":"Adding a tenant table (one with organization_id) in a migration without ALTER TABLE ... ENABLE ROW LEVEL SECURITY / FORCE ROW LEVEL SECURITY and a matching policy; changing a policy expression so it no longer references app.current_organization_id; adding an FK from one tenant table to another keyed only on id; making organization_id nullable on a table the checker requires to be scoped.","commonSituations":"A new feature table slipped into a migration without the RLS boilerplate; a DBA 'optimizing' or disabling RLS on a slow table; refactoring the tenant-context GUC name without updating every policy; backfills that drop NOT NULL on organization_id temporarily and shipping before restoring.","solutions":["Run ValidateTenantSchema (or the underlying catalog queries) in a dev database and match each violation string to the specific table/policy it names","For every flagged table: ENABLE and FORCE row level security, create/repair a policy whose USING/WITH CHECK expression equals organization_id = current_setting('app.current_organization_id', true) (allowed casts per the regexp), and make organization_id NOT NULL if required","Repair flagged foreign keys so they reference (organization_id, id) of the peer tenant table (and include project_id when both tables carry it)","Add the same checks to CI/migration tests so a non-compliant migration fails before deploy"],"exampleFix":"-- before: tenant table without forced RLS\nCREATE TABLE invoices (\n  id bigint PRIMARY KEY,\n  organization_id bigint NOT NULL REFERENCES organizations(id),\n  ...\n);  -- validation fails: rls not enabled/forced, policy missing\n\n-- after\nALTER TABLE invoices ENABLE ROW LEVEL SECURITY;\nALTER TABLE invoices FORCE ROW LEVEL SECURITY;\nCREATE POLICY tenant_isolation ON invoices\n  USING (organization_id = current_setting('app.current_organization_id', true))\n  WITH CHECK (organization_id = current_setting('app.current_organization_id', true));","handlingStrategy":"validation","validationCode":"// Standalone check developers can run in a dev DB or migration test:\nfunc assertTenantRLS(ctx context.Context, conn pgx Querier, table string) error {\n    var rls, force, hasPolicy bool\n    var notNullOK bool\n    err := conn.QueryRow(ctx, `\n        SELECT c.relrowsecurity, c.relforcerowsecurity,\n               EXISTS (SELECT 1 FROM pg_policy p WHERE p.polrelid = c.oid),\n               EXISTS (SELECT 1 FROM pg_attribute a\n                        WHERE a.attrelid = c.oid AND a.attname = 'organization_id' AND a.attnotnull)\n          FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace\n         WHERE n.nspname = 'public' AND c.relname = $1`, table).Scan(&rls, &force, &hasPolicy, &notNullOK)\n    if err != nil { return err }\n    if !rls || !force || !hasPolicy || !notNullOK {\n        return fmt.Errorf(\"table %s violates tenant isolation (rls=%t force=%t policy=%t org_not_null=%t)\", table, rls, force, hasPolicy, notNullOK)\n    }\n    return nil\n}","typeGuard":"func isIsolationViolationErr(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"postgres: tenant schema isolation incomplete\")\n}","tryCatchPattern":"if err := postgresconfig.ValidateTenantSchema(ctx, pool); err != nil {\n    if isIsolationViolationErr(err) {\n        // SECURITY: do not catch-and-continue serving traffic.\n        // Parse the '; '-joined violations, fix each table's RLS/policy/FK in migrations, redeploy.\n        log.Fatalf(\"tenant isolation violated: %v\", err)\n    }\n    return err // other errors: infra, see indices 15-17,19\n}","preventionTips":["Add ValidateTenantSchema (or per-table assertions) to migration tests in CI so a non-compliant migration never merges","Standardize a tenant-table migration template: organization_id BIGINT NOT NULL, ENABLE+FORCE ROW LEVEL SECURITY, policy using current_setting('app.current_organization_id', true)","Require tenant-to-tenant FKs to include organization_id (and project_id when both sides have it)","Never disable RLS 'temporarily' in production; treat it as a security regression","On violation, do not serve tenant traffic: fail the startup gate closed"],"tags":["postgres","security","rls","multi-tenancy","schema"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}