Skip to content

Isolate SaaS tenant runtime state per async execution - #4

Merged
OneClickPostFactory merged 9 commits into
OneClickPostFactory:mainfrom
AyobamiH:codex/immutable-tenant-runtime
Sep 6, 2026
Merged

OneClickPostFactory merged 9 commits into
OneClickPostFactory:mainfrom
AyobamiH:codex/immutable-tenant-runtime

Conversation

@OneClickPostFactory

Copy link
Copy Markdown
Owner

Purpose

Complete the process-global isolation part of D03 after the bounded overlap containment in PR #3.

The SaaS worker currently assigns tenant OpenAI/provider/media configuration through withTenantRuntime. Those assignments target the shared config object, and Threads/LinkedIn/X also swap module-global token-persistence callbacks. The containment gate prevents overlapping drains in one isolate, but the target safety boundary is that async executions do not share tenant-specific runtime state at all.

Changes

  • Add an AsyncLocalStorage-backed runtime scope for Cloudflare SaaS execution.
  • Instrument the existing config object after Worker bindings are loaded so tenant writes become copy-on-write inside the current async scope.
  • Keep existing provider/OpenAI/Cloudinary/Meta config reads compatible without a flag-day signature rewrite.
  • Make Threads, LinkedIn and X token-persistence callbacks scope-local during SaaS execution while preserving existing local-runtime fallback behaviour.
  • Keep rotated credentials visible inside their originating scope without mutating the process-global base config.
  • Add a deliberately concurrent two-tenant regression using the real Threads/LinkedIn/X persistence setters.
  • Prove a failed scope cannot leak tenant credentials into the next execution.
  • Record the exact repair boundary in the reliability progress document.

Preserved boundaries

  • No provider is re-enabled.
  • Threads and Instagram hosted publication remain fail-closed.
  • Facebook remains paused.
  • LinkedIn provider-contract compatibility work remains separate.
  • No Reddit connector boundary changes.
  • No Supabase schema, billing, queue, scheduling or publication-ledger semantics are changed.
  • No deployment is performed.

Runtime invariant

processPendingSupabaseJobs() remains serial. Tenant mutation is therefore restored between jobs inside a drain, while overlapping Worker invocations receive independent async-local stores. PR #3's exclusive run gate remains defence-in-depth rather than the sole tenant-isolation mechanism.

Acceptance evidence required

The full upstream npm run ci gate must pass on exact head e20fdae11f5660c8560ed6d6283e8e659a78e83b, including the new runtime-scope regression.

The regression requires:

  1. two overlapping tenant scopes resolve different OpenAI/provider credentials;
  2. Threads, LinkedIn and X rotations invoke only their own scope's persistence callback;
  3. rotated values stay in the originating scope and do not change the base config or peer scope;
  4. rejected scoped execution cannot leak state into later work.

Merge order

PR #2 -> PR #3 -> this PR.

This branch is stacked on PR #3 head ee377775030d27117b2568bc10426da8304c7a24. Until the earlier PRs merge, GitHub will also show their commits in this PR.

@OneClickPostFactory
OneClickPostFactory merged commit 033b9b5 into OneClickPostFactory:main Sep 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants