Zvessa docs

Public links and web pages

A space's pages open in any browser, without the app: a link-in-bio, a portfolio, a business page, a menu, a guestbook. Nothing is on the web until the owner adds the Public links kit, and then only what a visitor of the space may see.

Addresses #

AddressWhen it works
<site>/w/<handle>/<path>Always (<site> = WEB_URL, else the backend's CONVEX_SITE_URL)
https://<handle>.<WEB_ZONE>/<path>When WEB_ZONE is set and the web proxy runs (targets/web-proxy)
https://<own domain>/<path>Once its TXT record proves it, through the web proxy
<site>/embed/<handle>/<path>The same page for an iframe on another site
<site>/web/oembed?url=<page address>oEmbed JSON, so sites embed a page from its link

The domain isn't decided yet, so every part of it is a backend setting: WEB_ZONE, WEB_URL, WEB_CNAME (what own domains point at; default domains.<WEB_ZONE>), APP_URL (for "Open in the app"; default zvessa://), WEB_RESERVED (more handles nobody may take), WEB_PROXY_SECRET, and CF_API_TOKEN + CF_ZONE_ID for own domains through Cloudflare for SaaS.

Settings: /web #

Everything is a setting in the blueprint, changed like anything else (HQ → Web, the in-app AI, or an outside AI over MCP with propose_change), approved by a person, undone from History.

"web": {
  "handle": "ann",
  "domains": ["shop.ann.dev"],
  "home": "links",
  "routes": [
    { "path": "", "page": "links" },
    { "path": "work", "page": "portfolio", "title": "Ann's work", "description": "Portraits and prints", "image": "https://…" }
  ],
  "card": { "title": "Ann's studio", "description": "…", "image": "https://…" },
  "data": ["guestbook"],
  "index": false,
  "embed": "any",
  "appLink": true,
  "nav": true
}
SettingWhat it doesDefault
handleThe free address. 3-31 lowercase letters, digits, single dashes. One space per handle; one let go stays with its space for 7 daysnone
domainsUp to 5 own addresses, each proven with a DNS recordnone
homeThe page at the address itselfthe first page visitors see
routesWhich pages are on the web and at what path (up to 3 parts)every page visitors see, at /<page id>
cardThe share card (Open Graph, Twitter). A route's own title/description/image winsthe space's name and about, the page's first image
dataCollections whose records may show (records.list, records.count)none: records stay in the app
indexLet search engines list the pages (public spaces only); adds robots.txt and sitemap.xmlfalse (pages say noindex)
embed"any", "off" or a list of https origins allowed to frame the pages"any"
appLink"Open in the app" on pagestrue
navA menu of the pages when there's more than onetrue

Words on web pages are settings in /words: webOpenApp, webMore, webMadeWith, webNotFound, webLang.

What's safe, and how #

Blocks #

BlockWhat it is
web.shareThe space's (or one page's) web address with Copy and Share; hidden while not on the web
hq.webHQ's Web panel: address, handle, own domains with DNS steps and status, pages on the web, search engines, embeds and embed code

A link-in-bio is a stack of buttons; a portfolio is headings, text, images and cards. Web pages draw stack, heading, text, image, button (#page-id links become web paths), list, card, stat, divider, records.list, records.count and web.share.

Tools (in-app AI and MCP) #

ToolScopeWhat it does
get_webreadOn the web or why not, the address, each page's link and embed link, embed code, each domain's status and DNS records
check_handlereadWhether a handle is free and allowed
check_web_domainread (manage)Checks an own domain's DNS now
preview_web_pagereadWhat a visitor gets at a path (words, or the whole HTML), even before it's live

Setting the handle, domains, routes, cards and the rest is propose_change on /web.

App functions #

api.web.addresses.mine (HQ panel), .link({ page? }) (share block), .available({ handle }), .checkNow({ host }). HTTP routes are in convex/web/http.ts; the page drawing is convex/lib/web/render.ts; the settings and checks are convex/lib/web/model.ts.

Not yet #

Forms people fill in on the web without an account (needs spam protection), feeds and photo books drawn on the web, QR codes for an address, and per-page analytics.