Skip to content

feat: add expires_at field to virtual keys - #3229

Closed
crust3780 wants to merge 1 commit into
maximhq:devfrom
crust3780:virtual-key-expiry
Closed

feat: add expires_at field to virtual keys#3229
crust3780 wants to merge 1 commit into
maximhq:devfrom
crust3780:virtual-key-expiry

Conversation

@crust3780

@crust3780 crust3780 commented May 5, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Virtual Key field "Expires At" which defines an optional timestamp that defines whether a key is active. If the timestamp is older than the current time, the key becomes expired and requests don't work anymore. The date can be changed or removed and the key is active again.

Use case is to add Virtual Keys that are only valid for a limited time, e.g. for testing purposes.

Changes

  • Add ExpiresAt *time.Time to TableVirtualKey DB model with GORM index
  • Add DB migration (add_virtual_key_expires_at_column)
  • Wire ExpiresAt through Create/Update HTTP handlers (double pointer in UpdateVirtualKeyRequest to distinguish omitted vs. explicit null clear)
  • Add isVirtualKeyUsable() helper checking both IsActive and ExpiresAt
  • Update all governance enforcement points (PreLLMHook, governLargePayload, PreMCPHook, EvaluateVirtualKey resolver) to use the helper
  • Add expires_at to TypeScript types
  • Add datetime-local picker with clear button to virtual key form
  • Show Expired status badge (priority: Inactive > Expired > Exhausted > Active)
  • Display expiry date in virtual key details sheet

Expiry is enforced at request time without mutating the DB record. Re-enable by setting is_active=true and/or updating expires_at.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

Describe the steps to validate this change. Include commands and expected outcomes.

# Core/Transports
go version
go test ./...

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build

If adding new configs or environment variables, document them here.

Screenshots/Recordings

image image image

Breaking changes

  • Yes
  • No

If yes, describe impact and migration instructions.

Related issues

Close #3207

Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

Summary by CodeRabbit

Release Notes

  • New Features
    • Virtual keys now support optional expiration dates. Users can set, update, or clear expiration times when creating or managing keys.
    • Expired keys are automatically treated as inactive and rejected.
    • Virtual key status badges now display expiration state alongside active/exhausted indicators.
    • CSV exports include expiration status in the status field.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements virtual key expiry by adding an optional ExpiresAt timestamp field to the virtual key data model, database, HTTP API, governance validation rules, UI forms and displays, and configuration schemas. Expired keys are treated as inactive after the timestamp.

Changes

Virtual Key Expiry

Layer / File(s) Summary
Data model and public types
framework/configstore/tables/virtualkey.go, ui/lib/types/governance.ts
TableVirtualKey adds ExpiresAt *time.Time with GORM index and JSON serialization; public VirtualKey, CreateVirtualKeyRequest, and UpdateVirtualKeyRequest types add optional expires_at fields.
Database migration and persistence
framework/configstore/migrations.go, framework/configstore/rdb.go
Migration conditionally creates expires_at column and ExpiresAt index on TableVirtualKey; UpdateVirtualKey GORM whitelist includes expires_at to persist updates.
HTTP transport handlers
transports/bifrost-http/handlers/governance.go
CreateVirtualKeyRequest accepts optional expires_at (UTC-normalized); UpdateVirtualKeyRequest accepts presence-aware expires_at and handlers set/clear/omit TableVirtualKey.ExpiresAt according to presence semantics.
Governance expiry validation
plugins/governance/utils.go, plugins/governance/main.go, plugins/governance/resolver.go
Adds isVirtualKeyExpired helper comparing current UTC time against ExpiresAt. Expired keys are rejected with short-circuits in multiple pre-hook paths and in evaluation, with reason-specific errors returned.
UI form and submission
ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx
Adds expiresAt nullable datetime-local field; initializes from stored ISO timestamp; create omits when empty; update sends ISO UTC string or null to clear.
UI display and export
ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx, ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx
Status badge and CSV export now consider expires_at (priority: Inactive > Expired > Exhausted > Active); details view shows "Expires" row when present.
Configuration and schemas
transports/config.schema.json, docs/openapi/schemas/management/governance.yaml, helm-charts/bifrost/values.schema.json, helm-charts/bifrost/templates/_helpers.tpl, helm-charts/bifrost/values.yaml, helm-charts/bifrost/values-examples/providers-and-virtual-keys.yaml
Config and Helm/OpenAPI schemas accept optional expires_at as an RFC3339/ISO-8601 date-time (nullable where applicable); Helm helper and example values emit the field when present.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • maximhq/bifrost#3855: Similar transport handler update using schemas.OptionalJSON to distinguish omitted vs explicit null in update requests.

Suggested reviewers

  • akshaydeo
  • danpiths
  • roroghost17

Poem

🐰 A key's golden hour now has a sunset date,
Where time brings expiry before it's too late,
Trial keys will wane when the deadline's met,
Governance guards the gate—no longer a threat,
Happy keys hop on till their moment's set!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add expires_at field to virtual keys' is concise, specific, and directly summarizes the main feature addition shown throughout the changeset.
Description check ✅ Passed The PR description covers all key sections: Summary (use case explained), Changes (detailed list of modifications), Type of change (Feature marked), Affected areas (HTTP, Plugins, UI selected), How to test (test commands provided), Screenshots (UI changes shown), Breaking changes (No), Related issues (Closes #3207), Security considerations (addressed), and Checklist items (mostly completed).
Linked Issues check ✅ Passed All requirements from #3207 are implemented: optional expiry field added to virtual keys with DB migration, API surface updated for Create/Update handlers, TypeScript types extended, UI controls provided (datetime picker with clear button), and expiry enforcement implemented across governance hooks without automatic DB mutation.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing virtual key expiry: DB model/migration, HTTP handlers, governance enforcement, TypeScript types, UI components, OpenAPI schemas, config schemas, and Helm charts. No unrelated refactoring or unscoped modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@crust3780

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@crust3780
crust3780 marked this pull request as ready for review May 5, 2026 11:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
plugins/governance/resolver.go (1)

262-273: ⚡ Quick win

Prefer reusing isVirtualKeyUsable() to avoid policy drift.

This block duplicates active/expiry gating already defined in plugins/governance/utils.go. Reusing the helper here keeps virtual-key usability checks centralized and consistent across governance paths.

♻️ Suggested refactor
-	if !vk.IsActive {
+	if !vk.IsActive {
 		return &EvaluationResult{
 			Decision: DecisionVirtualKeyBlocked,
 			Reason:   "Virtual key is inactive",
 		}
 	}
-	if vk.ExpiresAt != nil && time.Now().UTC().After(vk.ExpiresAt.UTC()) {
+	if !isVirtualKeyUsable(vk) {
 		return &EvaluationResult{
 			Decision: DecisionVirtualKeyBlocked,
 			Reason:   "Virtual key has expired",
 		}
 	}
🤖 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 `@plugins/governance/resolver.go` around lines 262 - 273, This code duplicates
active/expiry checks for virtual keys; replace the manual gate with a call to
the central helper isVirtualKeyUsable(vk) from plugins/governance/utils.go,
remove the duplicated if-blocks, and if isVirtualKeyUsable indicates the key is
not usable return an &EvaluationResult{Decision: DecisionVirtualKeyBlocked,
Reason: <use reason returned by isVirtualKeyUsable or a consistent message>};
reference the local variable vk and the types EvaluationResult and
DecisionVirtualKeyBlocked when making the replacement so behavior stays
consistent across governance paths.
ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx (1)

375-375: ⚡ Quick win

Use a stable key for budget rows instead of the array index.

key={bIdx} is avoidable here and can cause row reconciliation issues when budgets change order or entries are inserted/removed.

♻️ Proposed fix
-								{displayBudgets.map((b, bIdx) => (
-									<div key={bIdx} className="space-y-2 rounded-lg border p-4">
+								{displayBudgets.map((b) => (
+									<div key={b.id ?? b.reset_duration} className="space-y-2 rounded-lg border p-4">
As per coding guidelines: "Always use stable, unique keys in lists; never use array index as key unless unavoidable".
🤖 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 `@ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx` at line 375,
Replace the unstable array index key (key={bIdx}) used for budget rows with a
stable unique identifier from each budget item (e.g., budget.id or another
persistent unique field on the budget object) in the VirtualKeyDetailsSheet
component's budget list render; update the JSX where bIdx is used (the div with
className "space-y-2 rounded-lg border p-4") to use that stable id (or a
fallback like budget.uuid or a concatenation of intrinsic unique fields) so
React can reconcile rows correctly when budgets are reordered or
inserted/removed.
🤖 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.

Inline comments:
In `@framework/configstore/migrations.go`:
- Around line 7301-7316: The migration adds/drops the expires_at column for
tables.TableVirtualKey but doesn't create the GORM index, causing schema drift;
update the migration (the Up closure where mg.AddColumn is called and the
Rollback closure where mg.DropColumn is called) to explicitly create the index
after adding the column (use mg.CreateIndex or db.Migrator().CreateIndex for
tables.TableVirtualKey, "ExpiresAt"/"expires_at" as appropriate) and explicitly
drop the index in Rollback (mg.DropIndex or Migrator().DropIndex) so upgraded
DBs get the same index that fresh installs get.

In `@ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx`:
- Around line 232-233: The prefill uses UTC components (new
Date(virtualKey.expires_at).toISOString().slice(0,16)) which makes the
datetime-local input show UTC as if it were local; change the expiresAt
assignment so you convert the stored UTC instant to the user's local wall-clock
string (YYYY-MM-DDTHH:MM) before slicing—i.e., if virtualKey?.expires_at exists,
build the local datetime string from new Date(virtualKey.expires_at) using
getFullYear(), getMonth()+1, getDate(), getHours(), getMinutes() with
zero-padding and produce "YYYY-MM-DDTHH:MM"; keep null when no expires_at. This
ensures the input value (expiresAt) matches the browser's local interpretation
and aligns with the parsing done later where the field is re-parsed into an ISO
instant.

---

Nitpick comments:
In `@plugins/governance/resolver.go`:
- Around line 262-273: This code duplicates active/expiry checks for virtual
keys; replace the manual gate with a call to the central helper
isVirtualKeyUsable(vk) from plugins/governance/utils.go, remove the duplicated
if-blocks, and if isVirtualKeyUsable indicates the key is not usable return an
&EvaluationResult{Decision: DecisionVirtualKeyBlocked, Reason: <use reason
returned by isVirtualKeyUsable or a consistent message>}; reference the local
variable vk and the types EvaluationResult and DecisionVirtualKeyBlocked when
making the replacement so behavior stays consistent across governance paths.

In `@ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx`:
- Line 375: Replace the unstable array index key (key={bIdx}) used for budget
rows with a stable unique identifier from each budget item (e.g., budget.id or
another persistent unique field on the budget object) in the
VirtualKeyDetailsSheet component's budget list render; update the JSX where bIdx
is used (the div with className "space-y-2 rounded-lg border p-4") to use that
stable id (or a fallback like budget.uuid or a concatenation of intrinsic unique
fields) so React can reconcile rows correctly when budgets are reordered or
inserted/removed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5db224e7-461a-487f-897d-577145b0a081

📥 Commits

Reviewing files that changed from the base of the PR and between ecbc3ee and 3eab325.

📒 Files selected for processing (11)
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/virtualkey.go
  • plugins/governance/main.go
  • plugins/governance/resolver.go
  • plugins/governance/utils.go
  • transports/bifrost-http/handlers/governance.go
  • ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx
  • ui/lib/types/governance.ts

Comment thread framework/configstore/migrations.go
Comment thread ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx Outdated
@akshaydeo

Copy link
Copy Markdown
Contributor

❤️ for the PR @crust3780 - can you look at the comment (specifically timezone) - keep everything in UTC on the server side

@greptile-apps

greptile-apps Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge — expiry is enforced fail-closed at all governance enforcement points and the DB migration handles both fresh installs and upgrades correctly.

The change is narrowly scoped: it adds a single nullable timestamp column, wires it through existing governance paths without altering their error-handling model, and the OptionalJSON approach correctly handles null vs absent in the update path. No pre-existing enforcement paths are weakened.

No files require special attention.

Important Files Changed

Filename Overview
framework/configstore/migrations.go Adds migrationAddVirtualKeyExpiresAtColumn migration that correctly adds the column and explicitly creates its index; rollback logic is present.
framework/configstore/tables/virtualkey.go Adds nullable ExpiresAt *time.Time with GORM index tag; time is already imported.
framework/configstore/rdb.go Adds expires_at to the explicit Select() column list in UpdateVirtualKey, ensuring the field is persisted on updates.
plugins/governance/utils.go Adds isVirtualKeyExpired with a proper vk != nil guard; uses time.Now().UTC() and .UTC() on the stored timestamp for a correct timezone-safe comparison.
plugins/governance/main.go Adds isVirtualKeyExpired checks to all HTTP pre-hook paths and refactors PreMCPHook to return distinct error messages for not-found, inactive, and expired keys.
plugins/governance/resolver.go Inserts isVirtualKeyExpired check after IsActiveValue check, returning a DecisionVirtualKeyBlocked result with a distinct expired reason.
transports/bifrost-http/handlers/governance.go Uses schemas.OptionalJSON[time.Time] for ExpiresAt in UpdateVirtualKeyRequest, correctly distinguishing null (clear) from absent (preserve).
ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx Adds expiresAt form field with datetime-local input and clear button; form initializes from existing value so update preserves expiry when unchanged.
ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx Adds client-side expiry check, updates status badge priority (Inactive > Expired > Exhausted > Active), and shows expiry row with formatDistanceToNow.
ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx Extends CSV export status logic with expiry check, matching the badge priority order.
ui/lib/types/governance.ts Adds expires_at to VirtualKey, CreateVirtualKeyRequest, and UpdateVirtualKeyRequest with semantics documented inline.

Reviews (24): Last reviewed commit: "feat: add expires_at field to virtual ke..." | Re-trigger Greptile

Comment thread framework/configstore/migrations.go
Comment thread plugins/governance/utils.go Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 5, 2026
Comment thread plugins/governance/utils.go Outdated
Comment thread ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 5, 2026
@crust3780

Copy link
Copy Markdown
Contributor Author

@akshaydeo should be fixed now

@crust3780

Copy link
Copy Markdown
Contributor Author

I think a schema update may be missing? Currently, configuration is only possible via UI and database.

@akshaydeo

Copy link
Copy Markdown
Contributor

will need that too

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@helm-charts/bifrost/values-examples/providers-and-virtual-keys.yaml`:
- Around line 554-559: The example hardcodes a near-term expiry for the virtual
key (id "vk-testing-limited") which will soon be expired and confuse users;
update the expires_at field in that block (the "expires_at" value for id
"vk-testing-limited") to be either removed, set to a far-future date (e.g.,
9999-12-31T23:59:59Z) or replaced with a clear placeholder like
"<ENTER_EXPIRY_ISO8601>" so consumers must intentionally supply an expiration
instead of copying a soon-expiring timestamp.

In `@helm-charts/bifrost/values.schema.json`:
- Around line 1236-1239: The expires_at property currently allows any string;
update its JSON Schema entry (the "expires_at" property in the chart/schema) to
enforce RFC3339/ISO-8601 by adding "format": "date-time", and ensure the
authoritative transports/config.schema.json is updated first (then propagate
changes to handlers and docs) so invalid timestamps are rejected at validation
time.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6728f98a-f4ef-4633-bbaa-b37b47ced50d

📥 Commits

Reviewing files that changed from the base of the PR and between bfbe31c and 33c2a58.

📒 Files selected for processing (5)
  • helm-charts/bifrost/templates/_helpers.tpl
  • helm-charts/bifrost/values-examples/providers-and-virtual-keys.yaml
  • helm-charts/bifrost/values.schema.json
  • helm-charts/bifrost/values.yaml
  • transports/config.schema.json
✅ Files skipped from review due to trivial changes (1)
  • helm-charts/bifrost/values.yaml

Comment thread helm-charts/bifrost/values-examples/providers-and-virtual-keys.yaml Outdated
Comment thread helm-charts/bifrost/values.schema.json
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 5, 2026
@crust3780

Copy link
Copy Markdown
Contributor Author

implemented and fixed, should be complete now.

@CLAassistant

CLAassistant commented May 6, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@transports/bifrost-http/handlers/governance.go`:
- Around line 40-51: UnmarshalJSON currently preserves the incoming time zone;
update NullableTime.UnmarshalJSON to normalize the parsed time to UTC before
assigning (i.e., call t = t.UTC() after successful json.Unmarshal and then set
n.Time = &t). Also ensure the CreateVirtualKey flow normalizes the decoded
pointer field by either changing CreateVirtualKeyRequest.ExpiresAt to
*NullableTime so UnmarshalJSON handles UTC normalization, or after JSON decoding
in createVirtualKey set req.ExpiresAt = &[]time.Time{req.ExpiresAt.UTC()}[0] (or
use a local var) so the stored ExpiresAt is always in UTC.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 734eabc7-864c-4b6d-bbee-6c5a77963f6c

📥 Commits

Reviewing files that changed from the base of the PR and between a78a63a and 98bbf29.

📒 Files selected for processing (1)
  • transports/bifrost-http/handlers/governance.go

Comment thread transports/bifrost-http/handlers/governance.go Outdated
@crust3780
crust3780 marked this pull request as draft May 6, 2026 11:20
@crust3780
crust3780 force-pushed the virtual-key-expiry branch from 98bbf29 to 56d2fab Compare May 6, 2026 11:25
@crust3780
crust3780 marked this pull request as ready for review May 6, 2026 11:33
@crust3780
crust3780 force-pushed the virtual-key-expiry branch from 82b2be4 to 275bf60 Compare May 6, 2026 11:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/governance/main.go (1)

1458-1466: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Return the correct decision for inactive/expired keys in MCP recheck.

At Line 1458, !ok || !isVirtualKeyUsable(vk) currently returns DecisionVirtualKeyNotFound.
For keys that exist but are inactive/expired, this should be DecisionVirtualKeyBlocked (or a branched message), otherwise rejection reasons and metrics get misclassified.

Suggested fix
-		if !ok || !isVirtualKeyUsable(vk) {
+		if !ok {
 			// VK became invalid or expired after initial check - fail closed for security
 			ctx.SetValue(governanceRejectedContextKey, true)
 			return req, &schemas.MCPPluginShortCircuit{Error: &schemas.BifrostError{
 				Type:       bifrost.Ptr(string(DecisionVirtualKeyNotFound)),
 				StatusCode: bifrost.Ptr(403),
 				Error: &schemas.ErrorField{
 					Message: "Virtual key not found",
 				},
 			}}, nil
 		}
+		if !isVirtualKeyUsable(vk) {
+			ctx.SetValue(governanceRejectedContextKey, true)
+			return req, &schemas.MCPPluginShortCircuit{Error: &schemas.BifrostError{
+				Type:       bifrost.Ptr(string(DecisionVirtualKeyBlocked)),
+				StatusCode: bifrost.Ptr(403),
+				Error: &schemas.ErrorField{
+					Message: "Virtual key is inactive or expired",
+				},
+			}}, nil
+		}
🤖 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 `@plugins/governance/main.go` around lines 1458 - 1466, The current recheck
branch treats both missing and expired/inactive virtual keys the same; update
the conditional handling in the MCP recheck so that when vk is absent (!ok) you
keep returning DecisionVirtualKeyNotFound, but when vk exists and
!isVirtualKeyUsable(vk) you return DecisionVirtualKeyBlocked (or a distinct
blocked message) instead of DecisionVirtualKeyNotFound; keep the
ctx.SetValue(governanceRejectedContextKey, true) and MCPPluginShortCircuit/error
structure but change the bifrost.Type to DecisionVirtualKeyBlocked and adjust
the Error.Message to indicate the key is inactive/expired so metrics and
rejection reasons are classified correctly (refer to isVirtualKeyUsable,
DecisionVirtualKeyNotFound, DecisionVirtualKeyBlocked,
governanceRejectedContextKey, MCPPluginShortCircuit).
🤖 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.

Outside diff comments:
In `@plugins/governance/main.go`:
- Around line 1458-1466: The current recheck branch treats both missing and
expired/inactive virtual keys the same; update the conditional handling in the
MCP recheck so that when vk is absent (!ok) you keep returning
DecisionVirtualKeyNotFound, but when vk exists and !isVirtualKeyUsable(vk) you
return DecisionVirtualKeyBlocked (or a distinct blocked message) instead of
DecisionVirtualKeyNotFound; keep the ctx.SetValue(governanceRejectedContextKey,
true) and MCPPluginShortCircuit/error structure but change the bifrost.Type to
DecisionVirtualKeyBlocked and adjust the Error.Message to indicate the key is
inactive/expired so metrics and rejection reasons are classified correctly
(refer to isVirtualKeyUsable, DecisionVirtualKeyNotFound,
DecisionVirtualKeyBlocked, governanceRejectedContextKey, MCPPluginShortCircuit).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eb558857-b816-4fbf-9d6a-cfa8c727f73c

📥 Commits

Reviewing files that changed from the base of the PR and between 98bbf29 and 275bf60.

📒 Files selected for processing (16)
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/virtualkey.go
  • helm-charts/bifrost/templates/_helpers.tpl
  • helm-charts/bifrost/values-examples/providers-and-virtual-keys.yaml
  • helm-charts/bifrost/values.schema.json
  • helm-charts/bifrost/values.yaml
  • plugins/governance/main.go
  • plugins/governance/resolver.go
  • plugins/governance/utils.go
  • transports/bifrost-http/handlers/governance.go
  • transports/config.schema.json
  • ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx
  • ui/lib/types/governance.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 6, 2026
@crust3780

Copy link
Copy Markdown
Contributor Author

@akshaydeo I think I have addressed every issue. However, I am a bit unsure about the various locations at which the expiry is tested. its tested in preHttp but also in prellm / premcp but i think if prehttp short circuits it will never reach the late code paths. could you clarify where the best location to test it is and if those later code paths can be deleted?

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 27, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 27, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@transports/bifrost-http/handlers/governance_test.go`:
- Around line 85-107: The test TestUpdateVirtualKeyRequestExpiresAtJSON fails to
compile because it uses require.NoError and assert.* helpers but the file lacks
imports for github.com/stretchr/testify/require and
github.com/stretchr/testify/assert; fix by adding those two imports to the test
file’s import block so the symbols require.NoError, assert.False, assert.True,
assert.Nil, require.NotNil, and assert.Equal resolve correctly (locate the
import block in transports/bifrost-http/handlers/governance_test.go).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 48e02f4e-4fa9-497c-a07b-374dfa52bdc1

📥 Commits

Reviewing files that changed from the base of the PR and between a6e43b0 and 9eafc0a.

📒 Files selected for processing (18)
  • docs/openapi/schemas/management/governance.yaml
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/virtualkey.go
  • helm-charts/bifrost/templates/_helpers.tpl
  • helm-charts/bifrost/values-examples/providers-and-virtual-keys.yaml
  • helm-charts/bifrost/values.schema.json
  • helm-charts/bifrost/values.yaml
  • plugins/governance/main.go
  • plugins/governance/resolver.go
  • plugins/governance/utils.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/governance_test.go
  • transports/config.schema.json
  • ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx
  • ui/lib/types/governance.ts
✅ Files skipped from review due to trivial changes (1)
  • helm-charts/bifrost/values-examples/providers-and-virtual-keys.yaml
🚧 Files skipped from review as they are similar to previous changes (14)
  • transports/config.schema.json
  • helm-charts/bifrost/templates/_helpers.tpl
  • framework/configstore/tables/virtualkey.go
  • ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx
  • framework/configstore/rdb.go
  • docs/openapi/schemas/management/governance.yaml
  • helm-charts/bifrost/values.yaml
  • ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx
  • framework/configstore/migrations.go
  • plugins/governance/utils.go
  • ui/lib/types/governance.ts
  • transports/bifrost-http/handlers/governance.go
  • plugins/governance/main.go
  • ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx

Comment thread transports/bifrost-http/handlers/governance_test.go Outdated
@crust3780
crust3780 force-pushed the virtual-key-expiry branch from 9eafc0a to 976b43c Compare May 27, 2026 10:16
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 27, 2026
@crust3780

Copy link
Copy Markdown
Contributor Author

@akshaydeo did rebase and cleanup and verified this works in a local test instance

@crust3780
crust3780 force-pushed the virtual-key-expiry branch 3 times, most recently from 883f61b to ff295cc Compare May 29, 2026 07:40
@crust3780

Copy link
Copy Markdown
Contributor Author

rebased and used optionaljson instead of custom type

@crust3780
crust3780 force-pushed the virtual-key-expiry branch from ff295cc to 2a192bc Compare May 29, 2026 17:06
@akshaydeo
akshaydeo force-pushed the dev branch 3 times, most recently from 6711ce3 to a1beab5 Compare June 4, 2026 10:02
@akshaydeo
akshaydeo force-pushed the dev branch 2 times, most recently from e389df7 to a65fce4 Compare June 8, 2026 11:25
@crust3780

Copy link
Copy Markdown
Contributor Author

closed in favor of #3765

@crust3780 crust3780 closed this Jun 9, 2026
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.

[Feature]: add option to automatically expire/disable virtual keys after a fixed time period

4 participants