v0.20.2 patch: /public/* surface for agent-published pages#83
Merged
Conversation
mcheemaa
added a commit
that referenced
this pull request
Apr 18, 2026
Bumps 0.20.1 to 0.20.2 in every reference: - package.json - src/core/server.ts VERSION - src/mcp/server.ts MCP server identity - src/cli/index.ts phantom --version - README.md version + tests badges (1,807 to 1,819) - CLAUDE.md tagline + bun test count - CONTRIBUTING.md test count Tests: 1,819 pass / 10 skip / 0 fail. Typecheck + lint clean. v0.20.2 ships #83: /public/* public static surface at truffle.ghostwright.dev/public/* mapped to public/public/* on disk. Serves without auth so Googlebot, OpenGraph scrapers, and the open web can read agent-published blog posts, tools, RSS feeds, and sitemaps. Traversal-defended via decodeURIComponent + path.resolve() containment against publicRoot + '/'. Auth gate on every other /ui/* path unchanged. 9 regression tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a public static subtree at URL
/public/*mapped to diskpublic/public/*. Files under that tree are served without auth so Googlebot, OpenGraph scrapers, and unauthenticated visitors can fetch them. The existing/ui/*auth gate is untouched for every other path.Why
Autonomous agents need a public surface to run blogs, tools, feeds, and sitemaps on their own domain. Today every agent-created page under
public/*is served from/ui/*and cookie-gated atsrc/ui/serve.ts:260-267, so scrapers hit a 302 to/ui/login. This unblocks SEO indexing, OpenGraph previews, and any other public read of agent-published artifacts.URL shape chosen for clean SEO and self-documenting paths:
truffle.ghostwright.dev/public/->public/public/index.htmltruffle.ghostwright.dev/public/blog/first-post.html->public/public/blog/first-post.htmltruffle.ghostwright.dev/public/blog/(directory) ->public/public/blog/index.htmltruffle.ghostwright.dev/public/feed.xml->public/public/feed.xmlSecurity posture
path.resolve()and a strict containment check: the resolved absolute path must equalpublicRootor start withpublicRoot + "/". Anything outsidepublic/public/returns 403./public/..%2Fsecret.htmlis rejected.Bun.file().public/public/*lives inside the existing container-writablepublic/volume (uid 999).Cache-Control: public, max-age=300.EXCLUDED_ROOT_DIRSinsrc/ui/api/pages.tsgains"public"so agent-published blog posts do not also appear in the landing-page "recent pages" rail; each surface stays coherent.Test plan
9 regression tests in
src/core/__tests__/server.test.tsplus one insrc/ui/api/__tests__/pages-api.test.ts:GET /public/withpublic/public/index.htmlpresent returns 200 and does NOT redirect to/ui/loginGET /public/with no index returns 404, never 302GET /public/blog/foo.htmlwithout cookie returns 200 when the file existsGET /public/blog/falls back to the directory'sindex.htmlGET /public/..%2Fsecret.html(traversal attempt) returns 403GET /public/..%2Fdashboard%2Fdashboard.js(traversal to reach a gated file) returns 403Cache-Controlon/public/*responses ispublic, max-age=300GET /ui/foo.htmlwithout cookie still redirects to/ui/loginGET /ui/dashboard/dashboard.jswithout cookie still returns 401/ui/api/pagesdoes NOT include files underpublic/Commands run locally, all green:
bun run lintcleanbun run typecheckcleanbun test: 1819 pass / 0 fail / 10 skip (was 1807 on main)Does NOT change
phantom_create_pagesignature insrc/ui/tools.ts(already accepts nested paths, untouched)src/ui/serve.ts(every existing auth rule unchanged, sameisAuthenticatedcall count as main)CLAUDE.md,README.md,package.json, version stringsRollout
No version bump in this PR. v0.20.2 bump + tag happen as a separate commit on main after merge.