feat(compliance): audit gating checks, worker tenant wiring, typed session organizationId - #231
Conversation
…ers, typed session organizationId - compliance audit now runs offline local checks: every project owning persistence entities must wire setupTenantFilter/setupRls in each runtime entrypoint (server.ts and worker.ts), register RetentionService when entities declare retention, and register ComplianceDataService when entities hold classified data; results print in a Local checks section and serialize as localFindings - sensitive-field heuristics: fields classified 'none' whose names match PII/PHI/PCI keyword lists (word-split camelCase/snake_case matching to limit false positives) are surfaced for review - worker template and sample-worker now wire setupTenantFilter/setupRls in worker.ts, closing the gap where background jobs queried tenant-scoped tables with no isolation layer active - init worker skips compliance.controller/routes when IAM is not configured (parity with init service; the controller resolves the IAM-gated JWKS_PUBLIC_KEY_URL token and failed typecheck otherwise) - core: ForklaunchBaseSession types organizationId as a first-party session claim, removing the Record<string, unknown> cast from the scaffolded controller tenant scoping (published as [email protected]) Co-Authored-By: Claude Fable 5 <[email protected]>
📝 WalkthroughWalkthroughThe PR adds tenant-aware worker initialization, typed organization session claims, offline compliance checks, and local finding output in compliance reports and audit command paths. ChangesTenant Isolation and Compliance
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant AuditCommand
participant LocalChecks
participant ModulesDirectory
participant ComplianceReport
participant Terminal
AuditCommand->>LocalChecks: Run local compliance checks
LocalChecks->>ModulesDirectory: Scan project files
LocalChecks-->>AuditCommand: Return sorted findings
AuditCommand->>ComplianceReport: Store localFindings
AuditCommand->>Terminal: Print formatted results
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Biome (2.5.5)cli/src/templates/project/worker/worker.tsFile contains syntax errors that prevent linting: Line 6: Private names are only allowed on the left side of a 'in' expression; Line 7: unterminated regex literal; Line 8: Illegal use of an import declaration not at the top level; Line 9: Private names are only allowed on the left side of a 'in' expression; Line 9: Expected an identifier but instead found '{'.; Line 9: Illegal use of an import declaration not at the top level; Line 9: Expected a semicolon or an implicit semicolon after a statement, but found none; Line 9: Invalid regex flag; Line 9: Invalid regex flag; Line 9: Invalid regex flag; Line 9: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{camel_case_name'.; Line 9: unterminated string literal; Line 16: Private names are only allowed on the left side of a 'in' expression; Line 22: unterminated regex literal; Line 23: Expected a property, or a signature but instead found '{pascal_case_name'.; Line 23: expected cli/src/templates/router/api/controllers/{{camel_case_name}}.controller.tsFile contains syntax errors that prevent linting: Line 1: Expected an identifier but instead found '{'.; Line 1: Private names are only allowed on the left side of a 'in' expression; Line 1: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{/is_iam_configured'.; Line 1: Expected a statement but instead found '}schemaValidator } from '@{{app_name}}/core''.; Line 2: Private names are only allowed on the left side of a 'in' expression; Line 3: Expected an identifier but instead found '{'.; Line 3: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{pascal_case_name'.; Line 3: Expected a statement but instead found '}ResponseMapper } from '../../domain/mappers/{{camel_case_name}}.mappers''.; Line 4: unterminated regex literal; Line 5: Expected a statement but instead found '^with_mappers'.; Line 6: Expected an identifier but instead found '{'.; Line 6: Illegal use of an import declaration not at the top ... [truncated 6138 characters] ... ad found Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
blueprint/sample-worker/worker.ts (1)
5-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReorder the new imports to match the import-layer guideline.
Move the local-persistence import (
./persistence/entities/sampleWorkerRecord.entity) before the same-directory import (./bootstrapper). The current order places a same-directory import ahead of a local-persistence import.🔧 Suggested import order
import { setupRls, setupTenantFilter } from '`@forklaunch/core/persistence`'; -import { ci, tokens } from './bootstrapper'; import { type SampleWorkerEventRecord } from './persistence/entities/sampleWorkerRecord.entity'; +import { ci, tokens } from './bootstrapper';As per coding guidelines, "Organize imports in 7 layers: Node built-ins, external dependencies, Forklaunch framework packages, cross-module imports, local persistence, local domain, and same directory."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@blueprint/sample-worker/worker.ts` around lines 5 - 7, Reorder the imports so the local persistence type import from sampleWorkerRecord.entity appears before the same-directory bootstrapper import, while leaving the external and framework imports unchanged.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@blueprint/sample-worker/worker.ts`:
- Around line 5-7: Reorder the imports so the local persistence type import from
sampleWorkerRecord.entity appears before the same-directory bootstrapper import,
while leaving the external and framework imports unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 15ddd0ea-006b-48b3-8e8d-4785027e8984
📒 Files selected for processing (9)
blueprint/sample-worker/worker.tscli/src/compliance/audit.rscli/src/compliance/checks.rscli/src/compliance/mod.rscli/src/init/worker.rscli/src/templates/project/worker/worker.tscli/src/templates/router/api/controllers/{{camel_case_name}}.controller.tsframework/core/package.jsonframework/core/src/http/types/apiDefinition.types.ts
Summary
Answers "do compliance checks verify the tenant/RLS/retention abstractions are actually wired?" — previously no; the audit was a data inventory (entities, routes, secrets, residency) with no wiring verification. Now it gates setup:
Audit: local checks (offline, deterministic, in
--jsonaslocalFindings)setupTenantFilterandsetupRlsin each runtime entrypoint (server.tsandworker.ts)RetentionService/ComplianceDataServiceregistrationsnonewhose names match PII/PHI/PCI keyword lists (camelCase/snake_case word-splitting with joined pairs —first_name,cardNumber,dateOfBirthmatch;name,externalId,billingProviderdon't)The gap the wiring check immediately caught
Worker entrypoints never wired tenant isolation:
server.tstemplates callsetupTenantFilter/setupRlsbutworker.ts(template + sample-worker) did not — background jobs touched tenant-scoped tables with no isolation active, silently, since the tenant filter is fail-open. Both are now wired (guarded byis_database_enabled, mirroring server.ts).Related fixes
init workernow skipscompliance.controller.ts/compliance.routes.tswhen IAM isn't configured (parity withinit service) — previously a database worker in a no-IAM app failed typecheck resolving the IAM-gatedJWKS_PUBLIC_KEY_URLtoken[email protected]:ForklaunchBaseSessiontypesorganizationIdas a first-party session claim; the scaffolded controller's(req.session as Record<string, unknown>)?.organizationIdcast is nowreq.session?.organizationIdTest plan
cargo test: 333 passed (new: keyword classifier cases, wiring-gate detection, fs-level check integration)server.ts; flags acontactEmailfield classifiednoneas possible PIIinit worker -t databasein a no-IAM app now typechecks clean (was TS2339)sample-workerbuilds with the wiring change; framework core 371 tests pass with the session type change🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes