Skip to content

feat(api-service): Organization switch flag#9981

Merged
scopsy merged 5 commits intonextfrom
cursor/organization-kill-switch-flag-989e
Feb 4, 2026
Merged

feat(api-service): Organization switch flag#9981
scopsy merged 5 commits intonextfrom
cursor/organization-kill-switch-flag-989e

Conversation

@scopsy
Copy link
Contributor

@scopsy scopsy commented Feb 4, 2026

What changed? Why was the change needed?

Implemented a new organization-level kill switch feature flag, IS_ORG_KILLSWITCH_FLAG_ENABLED.

This flag allows for the immediate halting of all trigger API requests and worker job processing (subscriber, workflow, and standard workers) for a specific organization and environment.

When enabled:

  • Trigger API endpoints (/trigger, /trigger/bulk, /trigger/broadcast) will return a 503 Service Unavailable response.
  • Worker processors will log a message and immediately skip processing the job.

This feature provides a critical operational tool to manage system load or respond to incidents by temporarily disabling service for affected organizations.

Screenshots

N/A


Slack Thread

Open in Cursor Open in Web

- Add new feature flag IS_ORG_KILLSWITCH_FLAG_ENABLED to FeatureFlagsKeysEnum
- Add kill switch check to events controller (trigger, triggerBulk, broadcastEventToAll endpoints)
- Add kill switch check to subscriber-process.worker.ts
- Add kill switch check to workflow.worker.ts
- Add kill switch check to standard.worker.ts

When kill switch is enabled for an organization:
- API trigger endpoints return 503 ServiceUnavailableException
- Worker processors skip job processing and return immediately

Co-authored-by: Dima Grossman <dima@grossman.io>
@cursor
Copy link
Contributor

cursor bot commented Feb 4, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@netlify
Copy link

netlify bot commented Feb 4, 2026

Deploy Preview for dashboard-v2-novu-staging canceled.

Name Link
🔨 Latest commit f5c8eee
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/69836b3fb6c3880008d8f123

@scopsy scopsy changed the title Organization kill switch flag Organization switch flag Feb 4, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

Hey there and thank you for opening this pull request! 👋

We require pull request titles to follow specific formatting rules and it looks like your proposed title needs to be adjusted.

Your PR title is: feat(api-service): Organization switch flag

Requirements:

  1. Follow the Conventional Commits specification
  2. As a team member, include Linear ticket ID at the end: fixes TICKET-ID or include it in your branch name

Expected format: feat(scope): Add fancy new feature fixes NOV-123

Details:

PR title must end with 'fixes TICKET-ID' (e.g., 'fixes NOV-123') or include ticket ID in branch name

Co-authored-by: Dima Grossman <dima@grossman.io>
…rait

- Add kill switch check to apikey.strategy.ts (component: 'api')
- Add component trait to events controller (component: 'trigger')
- Add component trait to worker processors (component: 'worker')

This enables granular control of the kill switch at different levels:
- 'api' - blocks all API requests at authentication level
- 'trigger' - blocks trigger endpoints specifically
- 'worker' - blocks worker job processing

Co-authored-by: Dima Grossman <dima@grossman.io>
@scopsy scopsy changed the title Organization switch flag feat(api-service): Organization switch flag Feb 4, 2026
@scopsy scopsy marked this pull request as ready for review February 4, 2026 15:41
Co-authored-by: Dima Grossman <dima@grossman.io>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

Walkthrough

Added KILLSWITCH to .cspell.json and a new feature-flag key IS_ORG_KILLSWITCH_FLAG_ENABLED to FeatureFlagsKeysEnum. Integrated kill-switch checks into API auth (apikey.strategy) and events endpoints (events.controller), where the flag causes ServiceUnavailableException for trigger endpoints and API key validation. Injected FeatureFlagsService into worker classes (standard.worker, subscriber-process.worker, workflow.worker) and tests; workers now query the flag and log/return early to skip job processing when enabled. Constructor signatures updated where applicable.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: implementing an organization-level kill switch feature flag across the API and worker services.
Description check ✅ Passed The description clearly explains the feature's purpose, implementation details, and behavioral outcomes across the API and worker components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/worker/src/app/workflow/services/standard.worker.ts`:
- Around line 98-116: The kill-switch lookup in getWorkerProcessor currently
calls isKillSwitchEnabled with data._organizationId/_environmentId which can be
missing for legacy jobs; modify getWorkerProcessor to first call
extractMinimalJobData (or reuse its fallback logic) to derive organizationId and
environmentId from payload.message when needed, then pass those resolved IDs
into isKillSwitchEnabled (or alter isKillSwitchEnabled to accept resolved IDs)
so the kill switch always checks the correct org/env for legacy payloads; refer
to the getWorkerProcessor, isKillSwitchEnabled, and extractMinimalJobData
symbols when making the change.

Comment on lines +98 to +116
private async isKillSwitchEnabled(data: IStandardDataDto): Promise<boolean> {
return this.featureFlagsService.getFlag({
key: FeatureFlagsKeysEnum.IS_ORG_KILLSWITCH_FLAG_ENABLED,
defaultValue: false,
organization: { _id: data._organizationId },
environment: { _id: data._environmentId },
component: 'worker',
});
}

private getWorkerProcessor() {
return async ({ data }: { data: IStandardDataDto }) => {
const isKillSwitchEnabled = await this.isKillSwitchEnabled(data);

if (isKillSwitchEnabled) {
Logger.log(`Kill switch enabled for organizationId ${data._organizationId}. Skipping job.`, LOG_CONTEXT);

return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Kill-switch lookup can miss org/env for legacy job payloads.
isKillSwitchEnabled uses _organizationId/_environmentId directly, but extractMinimalJobData already handles missing IDs via payload.message. Since the kill-switch check runs before that extraction, jobs lacking those fields may bypass the kill switch or error unexpectedly.

🔧 Minimal fix to reuse the fallback extraction for kill-switch IDs
-  private async isKillSwitchEnabled(data: IStandardDataDto): Promise<boolean> {
-    return this.featureFlagsService.getFlag({
-      key: FeatureFlagsKeysEnum.IS_ORG_KILLSWITCH_FLAG_ENABLED,
-      defaultValue: false,
-      organization: { _id: data._organizationId },
-      environment: { _id: data._environmentId },
-      component: 'worker',
-    });
-  }
+  private async isKillSwitchEnabled(data: IStandardDataDto): Promise<boolean> {
+    const minimalJobData = this.extractMinimalJobData(data);
+
+    return this.featureFlagsService.getFlag({
+      key: FeatureFlagsKeysEnum.IS_ORG_KILLSWITCH_FLAG_ENABLED,
+      defaultValue: false,
+      organization: { _id: minimalJobData.organizationId },
+      environment: { _id: minimalJobData.environmentId },
+      component: 'worker',
+    });
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private async isKillSwitchEnabled(data: IStandardDataDto): Promise<boolean> {
return this.featureFlagsService.getFlag({
key: FeatureFlagsKeysEnum.IS_ORG_KILLSWITCH_FLAG_ENABLED,
defaultValue: false,
organization: { _id: data._organizationId },
environment: { _id: data._environmentId },
component: 'worker',
});
}
private getWorkerProcessor() {
return async ({ data }: { data: IStandardDataDto }) => {
const isKillSwitchEnabled = await this.isKillSwitchEnabled(data);
if (isKillSwitchEnabled) {
Logger.log(`Kill switch enabled for organizationId ${data._organizationId}. Skipping job.`, LOG_CONTEXT);
return;
}
private async isKillSwitchEnabled(data: IStandardDataDto): Promise<boolean> {
const minimalJobData = this.extractMinimalJobData(data);
return this.featureFlagsService.getFlag({
key: FeatureFlagsKeysEnum.IS_ORG_KILLSWITCH_FLAG_ENABLED,
defaultValue: false,
organization: { _id: minimalJobData.organizationId },
environment: { _id: minimalJobData.environmentId },
component: 'worker',
});
}
private getWorkerProcessor() {
return async ({ data }: { data: IStandardDataDto }) => {
const isKillSwitchEnabled = await this.isKillSwitchEnabled(data);
if (isKillSwitchEnabled) {
Logger.log(`Kill switch enabled for organizationId ${data._organizationId}. Skipping job.`, LOG_CONTEXT);
return;
}
🤖 Prompt for AI Agents
In `@apps/worker/src/app/workflow/services/standard.worker.ts` around lines 98 -
116, The kill-switch lookup in getWorkerProcessor currently calls
isKillSwitchEnabled with data._organizationId/_environmentId which can be
missing for legacy jobs; modify getWorkerProcessor to first call
extractMinimalJobData (or reuse its fallback logic) to derive organizationId and
environmentId from payload.message when needed, then pass those resolved IDs
into isKillSwitchEnabled (or alter isKillSwitchEnabled to accept resolved IDs)
so the kill switch always checks the correct org/env for legacy payloads; refer
to the getWorkerProcessor, isKillSwitchEnabled, and extractMinimalJobData
symbols when making the change.

Co-authored-by: Dima Grossman <dima@grossman.io>
@scopsy scopsy merged commit c96ce0e into next Feb 4, 2026
32 checks passed
@scopsy scopsy deleted the cursor/organization-kill-switch-flag-989e branch February 4, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants