Skip to content

fix(relay): allow free plans to use the relay - #5571

Merged
Kitenite merged 1 commit into
mainfrom
unblock-relay-free-users
Jul 10, 2026
Merged

Kitenite merged 1 commit into
mainfrom
unblock-relay-free-users

Conversation

@Kitenite

@Kitenite Kitenite commented Jul 10, 2026 •

Copy link
Copy Markdown
Member

Summary

Relay access no longer requires a paid subscription. The relay gate (checkHostAccess) now grants access based solely on result.allowed from host.checkAccess, dropping the && result.paidPlan requirement. Free-plan users whose host is authorized in their org can now use the relay HTTP proxy and /tunnel WebSocket instead of getting 403 Forbidden / a closed socket.

Scope is limited to the relay server enforcement — the only place that actually blocked free users. The desktop paywall UI is intentionally left untouched. The paidPlan field returned by host.checkAccess is now unused by any consumer but kept in place to minimize the diff.

Test Plan

  • Free-plan user's authorized host connects via relay (no 403 / tunnel close)
  • Non-authorized host (wrong org) still denied
  • Paid-plan behavior unchanged

Open in Stage

Summary by cubic

Allow free-plan users with an authorized host to use the relay HTTP proxy and /tunnel WebSocket. Access now relies only on host.checkAccess returning allowed, removing the paid plan check.

Written for commit de920be. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Host access is now granted whenever access is approved, including for eligible plans that were previously blocked by an unnecessary plan-status check.

@cursor

cursor Bot commented Jul 10, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@ghost

ghost commented Jul 10, 2026

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 1 individual chapter for you:

Title
1 Remove paid plan requirement for relay access
Open in Stage

Chapters generated by Stage for commit de920be on Jul 10, 2026 6:03pm UTC.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dea0e663-4972-4803-abc7-021855f6f639

📥 Commits

Reviewing files that changed from the base of the PR and between a7fccb8 and de920be.

📒 Files selected for processing (1)
  • apps/relay/src/access.ts

📝 Walkthrough

Walkthrough

checkHostAccess now grants access whenever the API result is allowed, without requiring paidPlan. Routing checks, cache behavior, and TTL handling remain unchanged.

Changes

Host access authorization

Layer / File(s) Summary
Allowed-result authorization
apps/relay/src/access.ts
Access decisions now depend only on result.allowed; existing routing checks, API bypasses, caching, and return logic remain unchanged.

Estimated code review effort: 2 (Simple) | ~5 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: removing the paid-plan requirement for relay access.
Description check ✅ Passed The description covers the change summary and testing, but omits template sections like Related Issues, Type of Change, Screenshots, and Additional Notes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch unblock-relay-free-users

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.

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR removes the paidPlan requirement from the relay access gate, so free-plan users whose host is authorized in their org can now connect via the relay HTTP proxy and WebSocket tunnel. The change is limited to a single expression in checkHostAccess.

  • The ok condition in apps/relay/src/access.ts changes from result.allowed && result.paidPlan to result.allowed, granting relay access based solely on host authorization rather than subscription tier.
  • The paidPlan field returned by host.checkAccess is retained but no longer consumed by any caller; the desktop paywall UI is intentionally unchanged.

Confidence Score: 5/5

Safe to merge — the change is a one-line relaxation of a business-logic gate with no effect on the surrounding auth, caching, or error-handling paths.

The only modified line drops && result.paidPlan from the access decision. Host authorization (result.allowed) is still enforced, org membership is still checked locally before any API call, and the LRU caches continue to behave correctly. The unused paidPlan field is kept in the response type to minimise diff surface. No auth bypass, no data-plane change, no cache semantic shift.

No files require special attention.

Important Files Changed

Filename Overview
apps/relay/src/access.ts Single-line change removing && result.paidPlan from the relay access gate; logic is correct and cache semantics are unaffected.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Client
    participant Relay
    participant Cache
    participant API as API (host.checkAccess)

    Client->>Relay: HTTP/WebSocket request (token, hostId)
    Relay->>Relay: parseHostRoutingKey(hostId)
    alt hostId invalid or org mismatch
        Relay-->>Client: 403 Denied (local short-circuit)
    else org matches
        Relay->>Cache: lookup allowedCache[userId:hostId]
        alt cached allowed
            Cache-->>Relay: true
            Relay-->>Client: Access granted
        else not cached
            Relay->>Cache: lookup deniedCache[userId:hostId]
            alt cached denied
                Cache-->>Relay: true
                Relay-->>Client: 403 Denied
            else no cache
                Relay->>API: "host.checkAccess({ hostId })"
                API-->>Relay: "{ allowed, paidPlan }"
                Note over Relay: ok = result.allowed (paidPlan no longer required)
                alt "ok === true"
                    Relay->>Cache: "allowedCache.set(key, true) TTL=15m"
                    Relay-->>Client: Access granted
                else "ok === false"
                    Relay->>Cache: "deniedCache.set(key, true) TTL=30s"
                    Relay-->>Client: 403 Denied
                end
            end
        end
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Client
    participant Relay
    participant Cache
    participant API as API (host.checkAccess)

    Client->>Relay: HTTP/WebSocket request (token, hostId)
    Relay->>Relay: parseHostRoutingKey(hostId)
    alt hostId invalid or org mismatch
        Relay-->>Client: 403 Denied (local short-circuit)
    else org matches
        Relay->>Cache: lookup allowedCache[userId:hostId]
        alt cached allowed
            Cache-->>Relay: true
            Relay-->>Client: Access granted
        else not cached
            Relay->>Cache: lookup deniedCache[userId:hostId]
            alt cached denied
                Cache-->>Relay: true
                Relay-->>Client: 403 Denied
            else no cache
                Relay->>API: "host.checkAccess({ hostId })"
                API-->>Relay: "{ allowed, paidPlan }"
                Note over Relay: ok = result.allowed (paidPlan no longer required)
                alt "ok === true"
                    Relay->>Cache: "allowedCache.set(key, true) TTL=15m"
                    Relay-->>Client: Access granted
                else "ok === false"
                    Relay->>Cache: "deniedCache.set(key, true) TTL=30s"
                    Relay-->>Client: 403 Denied
                end
            end
        end
    end
Loading

Reviews (1): Last reviewed commit: "fix(relay): allow free plans to use the ..." | Re-trigger Greptile

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 1 file

Re-trigger cubic

@Kitenite
Kitenite merged commit 5c08700 into main Jul 10, 2026
17 of 18 checks passed
@github-actions

github-actions Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) ✅ View Branch
Vercel API (Vercel) ✅ Open Preview
Vercel Web (Vercel) ✅ Open Preview
Vercel Marketing (Vercel) ✅ Open Preview
Vercel Admin (Vercel) ✅ Open Preview
Vercel Docs (Vercel) ✅ Open Preview

Preview updates automatically with new commits

Kitenite added a commit that referenced this pull request Jul 10, 2026
Kitenite added a commit that referenced this pull request Jul 10, 2026
…ction, relay reason) (#5573)

* docs(plan): headless host registration fixes (4 bugs, diagnosed end-to-end)

Diagnosed while bringing a Sprites.dev sandbox online: SUPERSET_API_KEY env
never read (middleware.ts:7 / resolve-auth.ts), no --org selection in start
(start/command.ts:15), opaque relay 1008 Forbidden (access.ts:40 / index.ts:262),
and headless hosts stay unclaimed with no CLI path. Plan captures each with code
refs + fix sketch.

* docs(plan): note #5571 removed the paidPlan relay gate; Bug 4 (claim) is the remaining blocker

* fix(cli,relay): headless host registration — env key, org selection, relay reason

- cli: resolveAuth reads SUPERSET_API_KEY env as an override (flag > env >
  config > oauth), making the documented env var actually work headlessly.
- cli: `superset start` gains --org <id|slug|name>; with multiple memberships
  it prompts on a TTY and refuses (no silent guess) in agent/non-interactive
  mode instead of registering under the active org.
- relay: checkHostAccess returns a denial reason; the 1008/403 Forbidden now
  carries 'not a member of this org' / 'host not registered to this account'
  (tunnel-client already logs the close reason).
- plan: Bug 4 was a misdiagnosis — host.ensure auto-owns on registration, so a
  headless host self-claims; corrected the doc.

Tests: SUPERSET_API_KEY precedence + org resolution (both OR branches of the
non-interactive guard mutation-verified).

* refactor(cli): remove unreachable branch in resolveOrganization org picker

* feat(cli): --org on hosts list/status/wake/set-wake; docs + relay reason test

Fills the headless observability gap: you can register a host under a chosen
org (start --org) but previously couldn't list/manage it unless it was your
active org — and a pure env-key user (no config.organizationId) hit
'No active organization' outright.

- Promote org resolution to lib/resolve-org.ts (shared by start + observe
  commands); add an active-org fallback and a ctx-aware
  resolveOrganizationFromContext wrapper.
- Wire --org into hosts list, status, hosts wake, hosts set-wake. They default
  to the active org, resolve --org by id/slug/name, and fetch memberships so a
  headless key can target any org it belongs to.
- docs(remote-workspaces): document the headless flow (SUPERSET_API_KEY + --org).
- test(relay): cover accessDenialMessage (incl. the 123-byte WS-close limit) and
  checkHostAccess's no-network denial branches (invalid_host, not_in_org).

Tests: active-org fallback + relay byte-limit mutation-verified. E2E: hosts
list --org <other-org> now lists a non-active org's hosts (previously impossible).
@Kitenite
Kitenite deleted the unblock-relay-free-users branch July 11, 2026 23:30

This branch was previously deployed

1 inactive deployment
preview — de920be7 Deployed Jul 10, 2026 by Kitenite via Deploy API #6353
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