Public project pages
You want a build page anyone can open without a token or an account. Make the project public and it answers at an address of its own:
<workspace>.extension.dev/<project>The workspace lives in the host rather than a path segment, so a link carries the workspace identity before the page loads.
The vocabulary
Under the project root, every page is a noun:
| Path | Contents |
|---|---|
/builds/<buildId> | One build, every browser it produced |
/builds/<buildId>/whats-new | What changed in that build |
/builds/<buildId>/<browser> | One artifact: zip download, executable downloads, declared access |
/channels/<channel> | What the channel currently holds |
/<channel> | Shortcut to the channel page |
/versions/<version> | A version across the builds that carried it |
The per-browser build page is the one strangers land on. It offers the artifact as a zip, the executable downloads where the build produced them, and Declared access: each permission and host pattern the manifest declares, annotated with what it unlocks. A visitor learns what <all_urls> means without reading a manifest.
The zip itself is delivered as a 302 redirect to a short-lived presigned URL, so a script that fetches it must follow redirects (curl -L) to receive bytes. A client that does not follow redirects reads an empty body: that is the redirect left unfollowed, not an empty artifact, and the 302 says so in its X-Zip-Delivery header. A fetch that carries an Origin header (a browser reading across origins) is answered with the bytes directly instead.
Only a declared public project is served
A project is either public or private, and the platform serves this page only for a project declared public. Nothing else counts as a yes. A project nobody declared is treated as private, so silence is not consent to publish.
Two gates answer, and both read the same declaration:
| Gate | What it protects | What a stranger gets |
|---|---|---|
| The page | The listing, the name, the icon, the changelog | A card that names nothing |
| The registry | The build bytes themselves | 401, whether the file exists or not |
A deny always wins. A project declared private is refused even if an older allow entry is still around.
Your own access token opens both gates on your own project, so an owner view keeps working while a stranger is turned away. See Access tokens.
WARNING
Making a project public exposes its listing and its builds. It does not expose build logs, store credentials, or environment variables. Those never leave the console.
Setting visibility
Visibility is set per project in project settings under visibility, at console.extension.dev/<workspace>/<project>/settings/visibility. Workspace owners and admins change it; everyone else reads it.
A new project starts with the visibility of the source repository it was connected from: a public repository makes a public project, a private one makes a private project. The platform tells the registry at that moment, and again on every build, so the declaration heals itself.
The registry refreshes its lists about once a minute, so a change is not instant in either direction. Nothing recalls a file someone already downloaded.
Public is not indexed
Public means anyone holding the address can open the page. It does not mean search engines carry it. The workspace subdomain refuses every search crawler, in robots.txt and again in a noindex, nofollow tag on the page itself, so a public build page is reached by a link you send and not by a query someone types.
Link preview bots are admitted by name, because a shared build link has to render where it lands. Slack, Discord, Telegram, WhatsApp, X, Facebook, LinkedIn, Reddit, Mastodon, Bluesky, Skype and Embedly each get the project's name, its description and a generated card. A private project unfurls to a card that names nothing, the same gate as the page.
Private projects share by token
A private project has no public page, so it shares the other way: mint a scoped share token.
POST /api/projects/{id}/shareThe call returns a shareUrl and its expiry. It refuses public projects, because a public project already has a page and a scoped token would only counterfeit a gate that is not there.
When it fails
- The page shows a brand card instead of your project: visibility is not public. That is the gate working. Set it in project settings, visibility.
POST /api/projects/{id}/sharerefuses: the project is public. Link the page itself.- A channel shortcut 404s: the channel name collides with a reserved path segment such as
builds. Use the full/channels/<channel>form. - The page never appears in a search engine: it is not meant to. Every search crawler is refused. Send the address, or paste it somewhere a link preview renders it.
Next
- Share an unpublished build for review for one reviewer instead of everyone
- Releases for how a build reaches a channel
- Publishing for the store lanes