From 3b66c3e7b61d71d4870810abbb677d1befb35ad4 Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Sun, 28 Jun 2026 18:51:06 -0700 Subject: [PATCH] Serve /blog and /llms.txt publicly instead of redirecting to login On executor.sh the cloud worker only proxies an allow-list of paths to the marketing worker; everything else falls through to the auth gate. /blog and /llms.txt were not on the list, so unauthenticated visits got redirected to /login?returnTo=..., and session replays show those visitors bouncing immediately (blog ~79%, llms.txt ~90%). Add both to the marketing allow-list. /blog already has marketing routes; add a real llms.txt under marketing/public describing the product and key links. Export isMarketingPath and cover the allow-list with a unit test. --- apps/cloud/src/edge/marketing.test.ts | 36 +++++++++++++++++++++++++++ apps/cloud/src/edge/marketing.ts | 4 ++- apps/marketing/public/llms.txt | 32 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 apps/cloud/src/edge/marketing.test.ts create mode 100644 apps/marketing/public/llms.txt diff --git a/apps/cloud/src/edge/marketing.test.ts b/apps/cloud/src/edge/marketing.test.ts new file mode 100644 index 000000000..9101fca90 --- /dev/null +++ b/apps/cloud/src/edge/marketing.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from "@effect/vitest"; + +import { isMarketingPath } from "./marketing"; + +// On executor.sh the marketing middleware proxies an allow-list of paths to the +// `executor-marketing` worker; everything else falls through to the auth-gated +// cloud app (the sign-in page). `/blog` and `/llms.txt` are public content, so +// they must be on the allow-list: without it an unauthenticated visit redirects +// to `/login?returnTo=...` and the reader bounces. +describe("isMarketingPath", () => { + const marketing = [ + "/home", + "/privacy", + "/terms", + "/blog", + "/blog/", + "/blog/some-post", + "/llms.txt", + "/og-image.png", + "/_astro/app.css", + ]; + for (const pathname of marketing) { + it(`proxies ${pathname} to marketing`, () => { + expect(isMarketingPath(pathname)).toBe(true); + }); + } + + // App-owned routes must reach the Effect handler, not marketing. `/blogger` + // guards against a bare `startsWith("/blog")` swallowing unrelated words. + const notMarketing = ["/", "/login", "/cloud", "/mcp", "/dashboard", "/blogger"]; + for (const pathname of notMarketing) { + it(`leaves ${pathname} alone`, () => { + expect(isMarketingPath(pathname)).toBe(false); + }); + } +}); diff --git a/apps/cloud/src/edge/marketing.ts b/apps/cloud/src/edge/marketing.ts index 9b8b579a3..708155d0c 100644 --- a/apps/cloud/src/edge/marketing.ts +++ b/apps/cloud/src/edge/marketing.ts @@ -18,13 +18,15 @@ const MARKETING_PATHS = [ "/setup", "/privacy", "/terms", + "/blog", + "/llms.txt", "/api/detect", "/_astro", "/og-image.png", "/pattern-graph-paper.svg", ]; -const isMarketingPath = (pathname: string) => +export const isMarketingPath = (pathname: string) => MARKETING_PATHS.some((p) => pathname === p || pathname.startsWith(`${p}/`)); const getMarketingWorker = () => env.MARKETING as { fetch: typeof fetch } | undefined; diff --git a/apps/marketing/public/llms.txt b/apps/marketing/public/llms.txt new file mode 100644 index 000000000..d98199e40 --- /dev/null +++ b/apps/marketing/public/llms.txt @@ -0,0 +1,32 @@ +# Executor + +> Executor is an open-source integration layer for AI agents. Configure every integration once (MCP servers, OpenAPI specs, GraphQL APIs) with authentication and per-tool policies, then use that one catalog from any MCP-compatible agent. + +Every agent you use (Claude Code, Cursor, ChatGPT, and the rest) otherwise needs its own copy of every integration: the same API keys pasted in three places, the same MCP servers wired up again, no shared idea of what each tool is allowed to do. Executor is the layer in between: add a tool once, give it credentials once, set its policy once, and every agent shares it over MCP. + +How it works: + +- Add an integration: an MCP server, an OpenAPI spec, a GraphQL API, or a Google Discovery document. +- Create a connection: one configured (optionally authenticated) instance of that integration. An integration can have many connections. +- Set policies: decide whether each tool is always allowed, needs approval, or is blocked. +- Use it from any MCP-compatible agent: one catalog of tools, shared across every client. + +Run it your way: local CLI, a desktop app, hosted Executor Cloud, or self-hosted on Docker or Cloudflare. + +## Docs + +- [Documentation](https://executor.sh/docs): guides for adding integrations, connections, policies, and connecting agents over MCP. + +## Product + +- [Website](https://executor.sh): product overview and getting started. +- [Pricing](https://executor.sh/#pricing): plans for Executor Cloud. +- [Install](https://executor.sh/#install): install the CLI and connect your first agent. + +## Source + +- [GitHub](https://github.com/UsefulSoftwareCo/executor): source for the integration layer, plugins, and hosts. + +## Community + +- [Discord](https://discord.gg/eF29HBHwM6): support and discussion.