Skip to content

feat(public): gate public pages with explicit flags#3566

Open
PlaneInABottle wants to merge 4 commits intosimstudioai:stagingfrom
PlaneInABottle:feat/public-page-flags
Open

feat(public): gate public pages with explicit flags#3566
PlaneInABottle wants to merge 4 commits intosimstudioai:stagingfrom
PlaneInABottle:feat/public-page-flags

Conversation

@PlaneInABottle
Copy link

Summary

  • add explicit public-surface flags for landing, studio, changelog, legal pages, templates, and careers so self-hosted deployments can disable each surface independently
  • make public discovery outputs and shared UI respect the same helpers, including auth legal-link fallbacks and careers redirect/link gating
  • add focused regression coverage for route guards, nav/footer visibility, auth legal links, discovery outputs, template metadata visibility, and careers redirect behavior

@vercel
Copy link

vercel bot commented Mar 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Mar 13, 2026 8:01pm

Request Review

@cursor
Copy link

cursor bot commented Mar 13, 2026

PR Summary

Medium Risk
Introduces new environment-driven gating that changes routing/SEO behavior (returning notFound/404 and modifying sitemap/robots/feeds) across multiple public surfaces; misconfiguration could unintentionally hide pages or break discoverability.

Overview
Adds explicit NEXT_PUBLIC_ENABLE_* flags (default-on) and centralized helpers in feature-flags to independently disable public surfaces (landing, studio, changelog, legal, templates, careers) in self-hosted deployments.

Updates public routes and shared UI to honor these flags: pages/layouts now notFound() when disabled (and studio feeds return 404), nav/footer/logo hide or de-link items accordingly, auth pages use getAuth*LinkConfig to render internal legal links, fall back to external NEXT_PUBLIC_{TERMS,PRIVACY}_URL, or hide missing links.

Adjusts discovery/SEO outputs (sitemap, robots, llms.txt, llms-full.txt) to omit disabled routes/links, and tightens templates metadata to return noindex/“not found” when templates are disabled or unapproved; adds focused Vitest coverage for all of the above plus optional /careers redirect gating in next.config.

Written by Cursor Bugbot for commit 4e6c7f8. This will update automatically on new commits. Configure here.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR introduces six explicit opt-out feature flags (NEXT_PUBLIC_ENABLE_*) that let self-hosted deployments independently disable the landing page, studio pages, changelog, legal pages, templates, and careers link. When unset, every surface defaults to enabled, so existing deployments are unaffected.

Key changes:

  • feature-flags.ts — adds isPublicPageEnabled (defaults-to-enabled helper), six flag constants, and getAuthLegalLinkConfig / getAuthTermsLinkConfig / getAuthPrivacyLinkConfig for auth-surface legal links with external-URL fallback support.
  • Route guards — each public page/layout now calls notFound() (server-side) when its flag is off, replacing the previous client-side useEffect redirects on legal pages.
  • Discovery outputssitemap.ts, robots.ts, llms.txt, and llms-full.txt all respect the same flags, omitting disabled routes from crawlable output and adding them to the robots.txt disallow list.
  • UI gating — nav and footer links are conditionally rendered; the logo link wrapper is removed when the landing page is disabled.
  • next.config.ts/careers redirect is conditionally included via the exported getCareersRedirect helper.
  • Tests — broad regression coverage across route guards, nav/footer link visibility, auth legal-link fallback logic, discovery metadata, template metadata visibility, and the careers redirect.

Confidence Score: 4/5

  • Safe to merge — all flag gates default to enabled so no existing behavior changes. Minor formatting issues in LLM discovery documents when flags are disabled.
  • The implementation is well-structured, thoroughly tested, and defaults-to-enabled so no regressions are introduced. The only issues found are cosmetic: empty section headers in llms.txt and a stray blank line in llms-full.txt when all flags are off. These do not affect production users of the hosted deployment.
  • apps/sim/app/llms.txt/route.ts and apps/sim/app/llms-full.txt/route.ts — empty section headers / stray blank lines when flags are disabled.

Important Files Changed

Filename Overview
apps/sim/lib/core/config/feature-flags.ts Core flag implementation — adds isPublicPageEnabled helper, six flag constants, and getAuthLegalLinkConfig/getAuthTermsLinkConfig/getAuthPrivacyLinkConfig helpers. All flags correctly default to enabled when unset. isFalsy accepts `string
apps/sim/app/llms.txt/route.ts Conditionally omits disabled pages from the LLM discovery document. When all flags are off, corePages and optionalLinks become empty strings, leaving orphaned ## Core Pages and ## Optional section headers with no content.
apps/sim/app/llms-full.txt/route.ts Gates website and legal links behind flags. When isPublicLandingPageEnabled is false, websiteLink is an empty string that introduces a stray blank line before - **Documentation** in the ## Links section.
apps/sim/next.config.ts Adds getCareersRedirect helper that conditionally includes the /careers → Ashby redirect based on the NEXT_PUBLIC_ENABLE_CAREERS_LINK flag. Implementation is clean and well-tested.
apps/sim/app/robots.ts Adds disabled public routes to the robots disallow list. Landing page root / is intentionally excluded from the disallow list (adding it would block all pages). Correct behavior; confirmed by test.
apps/sim/app/sitemap.ts Wraps each static sitemap entry group behind its corresponding feature flag. Blog pages are also gated behind isPublicStudioPagesEnabled. Correct and well-tested.
apps/sim/app/(landing)/privacy/page.tsx Converts from a client component (useEffect-based redirect) to a server component with synchronous notFound() / redirect(). Cleaner and more SEO-friendly approach.
apps/sim/app/(landing)/terms/page.tsx Same as privacy page — converts from client to server component with proper notFound() / redirect() gates. Correct implementation.
apps/sim/app/templates/[id]/page.tsx Adds notFound() guard and extracts getPublicTemplateMetadataRecord helper that hides metadata (returns getUnavailableTemplateMetadata) for both disabled flag and non-approved templates.
apps/sim/app/(auth)/login/login-form.tsx Replaces hard-coded /terms and /privacy links with dynamic config from getAuthTermsLinkConfig / getAuthPrivacyLinkConfig. Correctly handles all combinations (both, one, or neither link present).
apps/sim/app/(landing)/components/footer/footer.tsx Wraps Pricing, Sim Studio, Changelog, Careers, Privacy, and Terms footer links behind their respective flags. Clean implementation.
apps/sim/app/(landing)/components/nav/nav.tsx Extracts homeContent to avoid duplication and gates the logo's Link wrapper behind isPublicLandingPageEnabled. Falls back to a non-clickable div when the landing page is disabled.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph EnvVars["Environment Variables (NEXT_PUBLIC_ENABLE_*)"]
        E1[LANDING_PAGE]
        E2[STUDIO_PAGES]
        E3[CHANGELOG_PAGE]
        E4[LEGAL_PAGES]
        E5[TEMPLATES_PAGES]
        E6[CAREERS_LINK]
    end

    subgraph Flags["feature-flags.ts — isPublicPageEnabled()"]
        F1[isPublicLandingPageEnabled]
        F2[isPublicStudioPagesEnabled]
        F3[isPublicChangelogPageEnabled]
        F4[isPublicLegalPagesEnabled]
        F5[isPublicTemplatesPagesEnabled]
        F6[isPublicCareersLinkEnabled]
    end

    E1 --> F1
    E2 --> F2
    E3 --> F3
    E4 --> F4
    E5 --> F5
    E6 --> F6

    F1 -->|false| LP_404["/  →  notFound()"]
    F1 -->|true| LP_OK["/  →  Landing page"]

    F2 -->|false| SP_404["/studio* layout → notFound()\nRSS & image-sitemap → 404"]
    F2 -->|true| SP_OK["/studio* served normally"]

    F3 -->|false| CL_404["/changelog → notFound()"]
    F3 -->|true| CL_OK["/changelog served normally"]

    F4 -->|false| LEG_404["/terms & /privacy → notFound()\nAuth forms: use TERMS_URL / PRIVACY_URL or hide"]
    F4 -->|true| LEG_OK["/terms & /privacy served\nAuth forms: internal links"]

    F5 -->|false| T_404["/templates & /templates/[id] → notFound()"]
    F5 -->|true| T_OK["/templates served normally"]

    F6 -->|false| C_NO["Nav/Footer Careers link hidden\n/careers redirect removed from next.config"]
    F6 -->|true| C_OK["Nav/Footer Careers link shown\n/careers → jobs.ashbyhq.com/sim"]

    F1 & F2 & F3 & F4 & F5 --> SITEMAP[sitemap.ts — omits disabled URLs]
    F2 & F3 & F4 & F5 --> ROBOTS[robots.ts — disallows disabled paths]
    F1 & F2 & F3 & F4 & F5 & F6 --> LLMS[llms.txt / llms-full.txt — omits disabled links]
Loading

Last reviewed commit: c65dfae

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

@PlaneInABottle
Copy link
Author

I opened this pr since I am using the project as my backend, had to remove the public pages manually. I think most people who self host the project would do the same.

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.

2 participants