Skip to content

M3: Client-only PATCH auth split for feature flags#10171

Merged
noanflaherty merged 2 commits into
feature/skill-feature-flags-gatewayfrom
swarm/skill-flag-gateway/task-3
Feb 27, 2026
Merged

M3: Client-only PATCH auth split for feature flags#10171
noanflaherty merged 2 commits into
feature/skill-feature-flags-gatewayfrom
swarm/skill-flag-gateway/task-3

Conversation

@noanflaherty
Copy link
Copy Markdown
Contributor

@noanflaherty noanflaherty commented Feb 27, 2026

Introduce a dedicated feature-flag client token distinct from the runtime bearer token. PATCH /v1/feature-flags/* requires the client token and explicitly rejects the runtime token. Token is auto-generated and stored at ~/.vellum/feature-flag-token. Part of #10145.


Open with Devin

Co-Authored-By: Claude <noreply@anthropic.com>
@noanflaherty noanflaherty self-assigned this Feb 27, 2026
@noanflaherty
Copy link
Copy Markdown
Contributor Author

👀 Where to focus your review

  • gateway/src/index.ts (PATCH auth gate): Security-sensitive change — PATCH /v1/feature-flags/* now explicitly rejects the runtime bearer token (403) before checking the dedicated feature-flag token. Verify the ordering of checks is correct: runtime-token-rejection must happen before feature-flag-token-validation to prevent the runtime token from being accepted.
  • gateway/src/config.ts (token auto-generation): New token file ~/.vellum/feature-flag-token is auto-generated with randomBytes(32).toString("hex") and written with mode 0o600. Verify this is the right generation/permission model.
  • gateway/src/index.ts (GET auth): GET /v1/feature-flags now accepts either the runtime bearer token or the feature-flag token. Confirm this dual-accept policy is the desired behavior.

Risk level: Medium — auth boundary change that could lock out legitimate clients if the token generation or validation logic has a bug.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

- Remove authRateLimiter.recordFailure() from 403 path to avoid penalizing
  legitimately authenticated clients who used the wrong token type (Issue 1)
- Only record failures on 401 (truly invalid authentication)
- Skip runtime-token rejection when FEATURE_FLAG_TOKEN is identical to
  runtimeBearerToken to support single-token deployments (Issue 2)

Addresses review feedback on PR #10171
@noanflaherty
Copy link
Copy Markdown
Contributor Author

@codex review this PR again — the previous issues have been fixed in commit 9f4d031

@noanflaherty
Copy link
Copy Markdown
Contributor Author

@devin review this PR again — the previous issues have been fixed in commit 9f4d031

@noanflaherty noanflaherty merged commit 5a74de5 into feature/skill-feature-flags-gateway Feb 27, 2026
2 checks passed
@noanflaherty noanflaherty deleted the swarm/skill-flag-gateway/task-3 branch February 27, 2026 01:09
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

noanflaherty added a commit that referenced this pull request Feb 27, 2026
* M1: Assistant config + skill feature flag enforcement (#10152)

* feat: add skill feature flags config and enforcement

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: enforce feature flags on included child skills and dynamic prompt section

Add isSkillFeatureEnabled checks in skill_load for child skills in both
the body-loading loop and the loaded_skill marker loop, so flag-OFF child
skills are fully hidden. Also filter hardcoded browser/twitter references
in buildDynamicSkillWorkflowSection through isSkillFeatureEnabled so the
system prompt does not advertise disabled skills.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

* M2: Gateway feature-flags API + persistence (#10165)

* feat: add gateway feature-flags REST API with config persistence

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: handle malformed URI encoding and config parse errors in feature-flags

- Wrap decodeURIComponent in try/catch in gateway/src/index.ts to return
  400 Bad Request on malformed percent-encoding instead of crashing to
  the global error handler with a misleading 500.

- Refactor readConfigFile to use a discriminated union result type that
  distinguishes "file doesn't exist" (returns empty config) from "file
  exists but can't be parsed" (returns error). The PATCH handler now
  returns 500 with a descriptive message when the config file is
  malformed, preventing silent data loss. The GET handler gracefully
  degrades to empty flags on parse errors (no data loss risk).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: validate parsed config is a plain object in readConfigFile

---------

Co-authored-by: Claude <noreply@anthropic.com>

* M3: Client-only PATCH auth split for feature flags (#10171)

* feat: add client-only PATCH auth split for feature-flags

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: remove rate-limit on 403 and handle identical token edge case

- Remove authRateLimiter.recordFailure() from 403 path to avoid penalizing
  legitimately authenticated clients who used the wrong token type (Issue 1)
- Only record failures on 401 (truly invalid authentication)
- Skip runtime-token rejection when FEATURE_FLAG_TOKEN is identical to
  runtimeBearerToken to support single-token deployments (Issue 2)

Addresses review feedback on PR #10171

---------

Co-authored-by: Claude <noreply@anthropic.com>

* M4: Client token plumbing (macOS/iOS) (#10175)

* feat: add feature-flag client token plumbing for macOS/iOS

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: throw on unavailable transport, always use gateway on macOS, clear stale token

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>

* docs: add feature-flags architecture documentation (#10181)

Co-authored-by: Claude <noreply@anthropic.com>

* fix: use active transport for macOS remote mode, preserve token on re-pair

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review feedback on final PR #10183

1. [P1] Fix gateway type-check: remove dead `not_found` variant from
   ConfigReadResult union (fixes narrowing bug on `result.detail`), make
   `featureFlagToken` optional in GatewayConfig type.

2. [P1] Add featureFlagToken to pairing responses: runtime now reads
   ~/.vellum/feature-flag-token and includes it in approval responses
   so iOS receives the token during QR pairing.

3. [P1] Fix PATCH auth bypass when tokens equal: loadConfig() now detects
   token collision and regenerates the feature-flag token to ensure the
   auth split is always enforceable.

4. [P2] Gate guardian-verification section on feature flags: system prompt
   skips buildGuardianVerificationRoutingSection() when guardian-verify-setup
   skill flag is OFF.

5. [P2] Fix failing integration test: removed incorrect assertion that
   <available_skills> disappears when only browser/twitter flags are OFF
   (bundled skills remain visible).

6. [P2] Fix macOS local HTTP transport: setFeatureFlag now checks if
   httpTransport.baseURL is localhost before delegating — local HTTP mode
   (localHttpEnabled) falls through to the direct gateway call.

7. [P2] Accept dotted skill IDs in feature-flag keys: updated regex from
   [a-z0-9_-]+ to [a-z0-9][a-z0-9._-]* to match managed skill ID validation.

* Migrate hatch visibility to skill flag and remove deprecated client flags

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

1 participant