Skip to content

feat(frontend): rbac token notice for the engine#5028

Merged
jog1t merged 1 commit into
mainfrom
05-06-feat_frontend_rbac_token_notice_for_the_engine
May 15, 2026
Merged

feat(frontend): rbac token notice for the engine#5028
jog1t merged 1 commit into
mainfrom
05-06-feat_frontend_rbac_token_notice_for_the_engine

Conversation

@jog1t
Copy link
Copy Markdown
Contributor

@jog1t jog1t commented May 11, 2026

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 11, 2026

PR #5028 Review — feat(frontend): rbac token notice for the engine

Overview

This PR does two related things:

  1. Renames the features.multitenancy flag to features.platform across ~15 frontend files, with a backward-compatible alias so the old VITE_FEATURE_MULTITENANCY env var keeps working during rollover.
  2. Adds a new features.acl flag and an Enterprise RBAC notice: when features.acl && !features.platform, the env-variables UI shows a placeholder <PUBLISHABLE_TOKEN> and a callout telling users to mint the token via their RBAC config rather than fetching it automatically.

What's Good

  • Backward-compatible alias. platform = (isEnabled("platform") || isEnabled("multitenancy")) && auth means existing deployments do not need an env-var change on day one.
  • Clear ACL semantics. acl = isEnabled("acl") || platform correctly expresses that cloud-platform users always get ACL. The enterprise-without-platform condition (features.acl && !features.platform) is used consistently in both new files.
  • usePublishableToken simplification. Returning null instead of silently reading the admin token for non-cloud builds is a better model. The ?? "<PUBLISHABLE_TOKEN>" fallback in useRivetDsn handles it clearly.
  • useEngineToken query migration. Replacing the direct useLocalStorage read with useQuery(useEngineCompatDataProvider().engineAdminTokenQueryOptions()) aligns token access with the rest of the data-provider pattern and removes the stale-read risk.
  • Storybook stories. The four stories (PlainOss / Cloud / EnterpriseAcl / Gallery) give a good visual regression surface for the three deployment shapes.
  • Cleanup. Unused imports (useLocalStorage, getConfig, ls) and the now-unnecessary RunnerPoolErrorPopover wrapper div are removed cleanly.

Issues and Suggestions

1. Mixed header casing in replayWorkflowFromStepHttp

All other headers in the same object use title-case (Authorization, Content-Type, X-Rivet-Target, X-Rivet-Actor), but the new token header is "x-rivet-token" (lowercase). HTTP headers are case-insensitive at the wire level, but any server-side code doing exact-string lookups will see the lowercase form. Prefer "X-Rivet-Token" for consistency.

2. Undocumented new headers in the replay request

X-Rivet-Target: actor and X-Rivet-Actor: ${actorId} were not in the original replay call and are not mentioned in the PR description. If the backend now requires them, that is a silent protocol change. If it does not, they are harmless but noisy. Worth a one-line comment explaining why they are needed.

3. rel="noopener" on the changelog link

In layout.tsx, the Changelog anchor uses rel="noopener" while every other external <a> in the same block uses rel="noopener noreferrer". The missing noreferrer leaks the Referer header on navigation. Low severity but inconsistent.

4. RunnerConfigErrorIndicator moved inside <HeaderLink>

Previously the indicator sat alongside the link in a flex wrapper. Now it is a child of the link element, so clicks on the indicator navigate to the Settings route. This may be intentional, but if the indicator is supposed to open a separate popover, nesting it inside the link may swallow the click.

5. Empty-string fallback in useEngineToken

return data || ""; — if the query has not resolved yet and there is no Suspense boundary above this call, data will be undefined and an empty string flows into the Authorization header. This is probably safe because the inspector render gate checks for a valid token before connecting, but worth confirming a suspense boundary covers this path.


Minor

  • PR description checklist is left unchecked — cosmetic, but worth filling in on future PRs.
  • The fake token constants in env-variables.stories.tsx look realistic. Fine for stories, but confirm they will not trigger automated secret-scanning passes.

Overall this is a clean, well-scoped change. The flag rename is mechanical and consistent, and the Enterprise RBAC notice fills a real gap in the UX. The header casing and undocumented replay headers are the only items worth a follow-up look.

@jog1t jog1t force-pushed the 05-06-feat_frontend_rbac_token_notice_for_the_engine branch from 2e537d4 to b166ca6 Compare May 11, 2026 18:49
@jog1t jog1t force-pushed the 05-06-fix_frontend_build_ladle_correctly branch from 331cbe7 to da858a7 Compare May 15, 2026 19:30
@jog1t jog1t force-pushed the 05-06-feat_frontend_rbac_token_notice_for_the_engine branch from b166ca6 to f3897b9 Compare May 15, 2026 19:30
@jog1t jog1t mentioned this pull request May 15, 2026
11 tasks
Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Code Review: feat(frontend): rbac token notice for the engine

Summary of changes: (1) Renames multitenancy to platform across the frontend with a backward-compatible alias. (2) Adds a new acl feature flag for enterprise deployments that have ACL but no cloud platform. (3) Adds a PublishableTokenNotice UI component with a Ladle story. (4) Updates the engine credentials dialog with enterprise-specific copy. (5) Fixes useEngineToken to use the query system instead of raw localStorage.


Issues

Stale import in accessors.ts

useEngineNamespaceDataProvider is still imported on line 7 of frontend/src/queries/accessors.ts but is no longer used anywhere in that file — the block that called useEngineNamespaceDataProvider().engineAdminTokenQueryOptions() was removed in this PR. This will produce a lint/type-check warning and should be removed.

// Remove this import — nothing in accessors.ts calls it after this PR
import {
    useCloudNamespaceDataProvider,
    useEngineCompatDataProvider,
    useEngineNamespaceDataProvider, // <-- unused
} from "@/components/actors";

Duplicated isEnterprise computation

const isEnterprise = features.acl && !features.platform is computed identically in two separate files:

  • frontend/src/app/dialogs/provide-engine-credentials-frame.tsx (line 16)
  • frontend/src/app/forms/engine-credentials-form.tsx (line 14)

These two files are closely coupled (the dialog renders the form), so this is low-risk today, but duplicated feature-flag predicates tend to drift. Consider either exporting a named flag from features.ts directly, or re-exporting the constant from engine-credentials-form.tsx into the dialog that uses it. The needsManualPublishableToken constant in env-variables.tsx (which is the same expression under a different name) adds a third variant. A single exported features.enterpriseAcl or similar would make the intent clear and eliminate the surface area.


Observations (non-blocking)

acl defaults to on in a full cloud build

In features.ts, isEnabled returns true when no env var is set (enabled === null). Because const acl = isEnabled("acl") || platform, features.acl is always true in a full cloud (no-flag) build. That evaluates needsManualPublishableToken = features.acl && !features.platform to false in the cloud path (since platform is also true there), which is the correct behavior. However, an operator who sets VITE_FEATURE_FLAGS=auth explicitly gets acl=false unless they also include acl in the flag list, which would hide the publishable-token notice from an ACL-enabled self-hosted build. If that scenario is expected, document acl alongside the other recognized flag strings.

Changelog link rel inconsistency

The changelog <a> links in layout.tsx (lines 301 and 335) use rel="noopener" without noreferrer. All other external links in the same file use rel="noopener noreferrer". This is a pre-existing inconsistency, but the reformatting in this PR makes it more visible; it would be low effort to fix in the same change.

useEngineToken race window in non-platform path

The old path used useLocalStorage, which reads synchronously. The new path uses useQuery(useEngineCompatDataProvider().engineAdminTokenQueryOptions()), which returns undefined until the query resolves. The function returns data || "", so the caller gets an empty string while loading. This matches the previous behavior when the key was absent, and the downstream useActorEngineContext memo passes the value through unconditionally, so there is a brief window on first render where an empty token may be used. Worth a quick check that the actor engine context guards against connecting with an empty token before the query settles.


Positive notes

  • The platform alias for multitenancy (isEnabled("platform") || isEnabled("multitenancy")) is a clean backward-compatible approach for a rename without a flag-day migration.
  • Moving useEngineToken off useLocalStorage and onto the query system is the right direction; it makes the token lifecycle consistent with the rest of the credential queries.
  • The Ladle story covers all three meaningful configurations (plain OSS, cloud, enterprise ACL) plus an isolated notice story, which matches the coverage guidance in frontend/CLAUDE.md.
  • The PublishableTokenNotice placeholder <PUBLISHABLE_TOKEN> in the DSN value is a sensible degraded-state UX; the notice makes the manual step explicit rather than silently showing a broken URL.

Copy link
Copy Markdown
Contributor Author

jog1t commented May 15, 2026

Merge activity

  • May 15, 7:36 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 15, 7:48 PM UTC: Graphite rebased this pull request as part of a merge.
  • May 15, 7:48 PM UTC: @jog1t merged this pull request with Graphite.

@jog1t jog1t changed the base branch from 05-06-fix_frontend_build_ladle_correctly to graphite-base/5028 May 15, 2026 19:45
@jog1t jog1t changed the base branch from graphite-base/5028 to main May 15, 2026 19:46
@jog1t jog1t force-pushed the 05-06-feat_frontend_rbac_token_notice_for_the_engine branch from f3897b9 to 1fbf3fd Compare May 15, 2026 19:47
@jog1t jog1t merged commit 844c4ca into main May 15, 2026
5 of 8 checks passed
@jog1t jog1t deleted the 05-06-feat_frontend_rbac_token_notice_for_the_engine branch May 15, 2026 19:48
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