Skip to content

fix(cli): linear setup clobbers a second workspace's webhook signing secret with the stack-wide one → 401 Invalid signature #611

Description

@isadeks

Summary

Re-running bgagent linear setup <slug> on an already-installed workspace silently overwrites that workspace's per-workspace webhook signing secret with the stack-wide fallback secret — which belongs to whichever workspace installed first. In a multi-workspace deployment this breaks HMAC signature verification for every workspace after the first: the receiver rejects Linear's deliveries with HTTP 401 {"error":"Invalid signature"}.

Live-hit on the demo-abca workspace: after re-authing (which runs setup), the stored webhook_signing_secret flipped from the workspace's real value to the stack-wide one (lin_wh_e9m… from a different install), and every webhook delivery started 401-ing. Recovery required bgagent linear update-webhook-secret demo-abca to paste the real secret back.

Root cause

cli/src/commands/linear.ts, setup action (~line 687). The block is guarded only on stackWideAlreadyConfigured:

if (stackWideAlreadyConfigured) {
  // "Either way the stored value is this workspace's signing secret — lift it
  //  into the per-workspace bundle without prompting"   ← WRONG for workspace #2
  webhookSigningSecret = <stack-wide secret>;
}

The inline comment's assumption ("the stored value is this workspace's signing secret") holds only for the first workspace. For an additional workspace, the stack-wide secret is a different workspace's secret, so the mirror corrupts the (correct) per-workspace secret. Also, setup rebuilds the OAuth bundle fresh from the exchange and upserts it before this block, so the pre-existing webhook_signing_secret is already dropped by the time the block runs.

Fix (implemented)

  • New pure helper resolveWebhookSecretAction(existingPerWorkspaceSecret, stackWideConfigured) in cli/src/linear-oauth.tspreserve | mirror-stackwide | prompt. An existing valid per-workspace secret always wins (rotation is update-webhook-secret's job); else mirror stack-wide (with a warning to verify for additional workspaces); else prompt.
  • setup now reads the existing per-workspace webhook_signing_secret before the OAuth overwrite and preserves it.
  • 5 unit tests (incl. the multi-workspace re-run case).

Acceptance criteria

  • Re-running setup on an installed workspace does NOT change its webhook_signing_secret.
  • First install still stores/prompts correctly; the stack-wide back-compat path still works for the first workspace.
  • Regression test for the second-workspace re-run case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    approvedWhen an issue has been approved and readybugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions