{"id":48025,"plugin_id":"plugins_6abfd785747081918610c2cb316a2b40","kind":"skill","collection_source":"plugin_package","comparison_source":null,"observed_at":"2026-10-24T13:35:18.284Z","digest":"6dfbee87005c215da929410418177340fc11667dd83cb854b3da8b0937dba403","against":null,"payload":{"description":"Plan a static website and its saved data before implementation. Use when someone asks what to build with Simple Host KV, SQLite or files, how to preserve an existing site's declared-data behavior, or how to scope a Simple Hack team project. Hand implementation to website-deploy.","included_files":[],"name":"website-deploy-builder","skill_md_contents":"---\nname: website-deploy-builder\ndescription: Plan a static website and its saved data before implementation. Use when someone asks what to build with Simple Host KV, SQLite or files, how to preserve an existing site's declared-data behavior, or how to scope a Simple Hack team project. Hand implementation to website-deploy.\n---\n\nIf the Simple Hack connector tools are available, use them; otherwise use the REST API with the person's key (email-code sign-in).\n\nThat opening line applies to a Simple Hack team project. For an ordinary\nSimple Host site, follow the Simple Host connector and planning flow below.\n\n# Website Deploy Builder\n\nFor a Simple Hack team project, plan the static site and declared data with\nthe same patterns below, then hand off to `website-deploy`. The team publishes\nonly to `https://<team>.<event>.simple-hack.app/` through its current team\nconnection or team key; the Simple Host personal address and domain options\nbelow do not apply to that team site.\nFor an organiser's public event website, plan its static pages and use\n`run-hackathon` for the event-specific publish and built-in/custom switch.\n\n**First rule: use the Simple Host tools when you have them.** If the Simple Host\nconnector's tools are available in this session (`who_am_i`, `list_sites`,\n`create_site` / `update_site` (`deploy_site` on older connections), `get_state`,\n`connect_domain`, …), use them for everything and never ask the person for an\nemail, a code or an API key — the connector is already signed in as them. Sign-in\nitself is unchanged: when the person connects Simple Host in their AI app, a\nSimple Host sign-in window opens, they sign in with Google or the emailed code,\nthen choose Allow, and every chat after that is signed in. If a tool reports the\nconnection is not signed in, ask them to reconnect Simple Host in their app's\nsettings. Only when those tools are not available (e.g. a coding agent without\nthe connector) use the email-code and API-key flow the `website-deploy` skill describes.\n\nUse this skill when a user wants help deciding what to build on Website Deploy, or how to scope an idea they already have. After the user picks an approach, hand off to the `website-deploy` skill for deploy.\n\nFor a **new Simple Host** site's backend, plan with three flexible resources:\nJSON KV for named values, SQLite for related records and queries, and files for\ndurable binary objects. The agent chooses the schema, keys and paths for the\nactual app. Each resource has independent whole-resource `read` and `write`\npolicies (`anyone`, `signed-in`, `owner`), initially owner-only, plus optional\ninheritance of the site's passcode. Anonymous writes require an explicit\n`anyone` policy. A signed-in policy does not make rows private per person.\nRead `website-deploy/references/storage.md` before implementation. For an\n**existing site** using state, collections or declared kinds, preserve its\nbuilt-in privacy, atomic operations, undo and notification behavior unless the\nowner chooses and verifies a migration. Simple Hack team and custom event websites can also use these resources.\nPreserve an existing Hack site's declared data unless the owner asks to migrate it.\n\n## What Website Deploy gives you\n\nWebsite Deploy is a static-file host at `https://simple-host.app`. Each site lives at its own address, `https://<sitename>.<handle>.simple-host.app/` (`handle` is the owner's URL-safe handle from GET `/v1/me`; `https://<handle>.simple-host.app/` lists the person's public sites). Always hand the person the `site_url`/`url` the deploy returned — for a brand-new account it is briefly `https://<handle>.simple-host.app/<sitename>/` until the site's certificate is issued. The dashboard/API stay on `https://simple-host.app` (a separate origin). Old `<handle>.simple-host.app/<site>/` and `sites.simple-host.app/<handle>/<site>/` links redirect to the site's address. There is no server-side execution — but the API gives each site a real, server-backed backend:\n\n| Capability | How |\n|---|---|\n| HTML / CSS / JS / images / fonts served as a site | Deploy files inline as JSON (`/files`) or upload a `.tar.gz`/`.zip`. With the connector: `create_site` / `update_site` (`deploy_site` on older connections) |\n| **New Simple Host backend** | Declare KV, SQLite or files resources with `storage_set_resource` or `PUT /v1/sites/<site>/storage/resources/<name>`; set independent whole-resource read/write policies; use the matching `storage_*` connector tools or same-origin REST. The three kinds share 1,000,000 bytes per website; plan client-side phone-photo compression for upload pages. See `website-deploy/references/storage.md` |\n| **Existing declared-data API** | `declare_data` or `PUT /v1/sites/<sitename>/data/<name>/kind` preserves Page info, Submissions, Personal and Shared boards. Keep it when an existing site depends on per-person privacy, item versions, history or notifications; see `website-deploy/references/backend.md`. |\n| Who may save here | Anyone who signs in (default), or only listed emails and whole `@domains`, plus a block list: `set_who_can_save`, `block_person` |\n| Per-site JSON state (≤ 1 MB, shared across all visitors; older sites) | `GET / PUT /v1/sites/<sitename>/state` (same-origin from the page; agents can also use `/v1/u/<handle>/sites/<sitename>/state` on the apex). Reads public; a page write needs the visitor signed in first (`auth.js`) |\n| Atomic state updates (concurrent-safe counters, lists, votes) | `PATCH .../state` with `{ops:[inc/append/set/remove/removeWhere]}`; `If-None-Match` ETag for cheap polling. A write — same rule as above |\n| Collections (signups / RSVPs / submissions) | `POST/GET /v1/sites/<sitename>/collections/<name>`. GET public; POST is a write. The owner removes entries (one, or the whole list); in declared Submissions each visitor also changes and withdraws their own |\n| Private collections (orders, RSVPs, anything personal) | `set_collection_privacy` (or `PUT .../collections/<name>/privacy` `{\"private\":true}`). Signed-in visitors add; only the site owner — and the Simple Host operator, for moderation — can read it. The owner can edit or delete items (`update` / `remove`). In a public list the owner can delete (spam) but not edit; in declared Submissions each visitor also changes and withdraws their own |\n| A nicer address (optional) | Free `<name>.simple-host.app`: one call (`connect_domain`), active at once, no DNS. Or a custom domain via the `connect-domain` skill (two DNS records: the address and a TXT ownership record). The site moves there and its old address redirects |\n| Agent writing for the site owner (no browser) | The connector (`update_state`, `add_to_collection`) if present; otherwise the owner's own API key, obtained by email code, as `X-API-Key` — works only on sites that account owns (another account's key gets 404). Anyone else saves on the page as a signed-in visitor. See \"Saving from an agent\" in the `website-deploy` skill's `references/backend.md` |\n| Per-visitor state | `localStorage`, `sessionStorage`, `IndexedDB` (in the browser), or **Personal** (`mine`) when it must follow the visitor to another device |\n| External APIs | `fetch()` from the page to any public CORS-enabled API |\n| Keeping a whole site from people without a passcode | One shared passcode on the whole site (`set_site_passcode`; the person chooses it, any 6+ characters, or asks for 6 digits; ask first). Not a login: anyone given it can pass it on, and saved data is not private per person. No per-page lock |\n| Routing | Static files only — path-relative directories with `index.html`; SPA routing via the framework's hash router or `404.html` fallback |\n\nIf the idea needs server-side application code, custom user accounts, platform-enforced per-row roles or long-running jobs, explain that those parts need another service. A site can use its own SQLite resource for SQL tables and queries; do not describe shared SQL as unsupported.\n\n**State the chosen resource policy before designing the page.** A new resource starts owner-only; `anyone` can allow anonymous reading or writing, and `signed-in` uses the visitor's site-scoped Google or emailed-code sign-in. Agents acting for the owner use the connector or owner API key. Existing state and declared-data writes keep their prior sign-in requirements.\n\n**Preserve per-person privacy.** Existing Submissions and Personal kinds have visitor-specific visibility, edits and withdrawal that a database-wide `signed-in` policy does not provide. Keep those APIs for an existing site that uses them. For a new design involving personal details, do not choose a shared KV namespace or SQL table with broad read access; design the privacy boundary explicitly and use existing private Submissions or Personal where their built-in semantics are required. SQL joins and search within a resource are supported; platform-enforced per-row roles and instant push updates are not.\n\n**When retaining the declared-data API, use private Submissions for personal details.** Orders, RSVPs, survey answers, sign-ups, or anything with names, emails, phone numbers or addresses: only signed-in visitors can submit, only the owner reads them all, and each visitor sees, changes and withdraws their own. Plan it in this order:\n\n1. Declare it (`declare_data` with `kind: \"entries\"`) before the form goes live.\n2. The form page calls `await SH.requireSignIn()` before `SH.data('orders', 'entries').add({...})`, and shows the saved item from the answer as the visitor's receipt (and `.mine()` for what they sent before).\n3. An owner page on the site (e.g. `orders.html`) that signs in, lists them (`SH.data('orders').list()`), and has \"Mark done\" (`.update(id, {status:'done'})`) and \"Delete\" (`.remove(id)`) buttons. It works only for the owner's account. The owner also has their sites page (every entry with who sent it, a daily email, a spreadsheet download); the agent reads it with `read_collection`.\n\nPublic Submissions (a guestbook, public comments) are `\"visibility\": \"public\"`; say so plainly. Pages are public to anyone with the link unless the owner puts a passcode on the whole site (then to anyone who also has the passcode); only private Submissions are closed, readable in full by the site owner and the Simple Host operator (for moderation).\n\n**Always pair a form with a viewer.** Any site that COLLECTS data (a signup, RSVP, guestbook, contact form, order) MUST also ship a second page — e.g. `admin.html` — that reads the same collection back (`GET .../collections/<name>?limit=200` → `{items:[{id,data,created_at},…]}`) and lists every entry for the owner, newest first, plus the live total from state. Link it quietly from the main page (a small \"Organizer view →\" in the footer). A form with nowhere to read the results is only half the feature — and the person you're building for will not think to ask for the viewer, so add it by default. Mark the viewer `<meta name=\"robots\" content=\"noindex\">`. A public collection is readable by anyone with the link, so don't fake a password; if the entries are personal, make the collection private and the viewer becomes the owner page above.\n\n## How to use this skill\n\n1. Ask the user what they're trying to build, in plain language. Don't push capabilities at them — let them describe the idea.\n2. Decide whether it can run as a static site. If parts of it can't, name those parts and either propose a static-friendly substitute or recommend a different host for that piece.\n3. If visitors will save anything, choose KV, SQLite or files for a new Simple Host site, and state each resource's read/write/passcode policy. Plan sign-in when the policy or a retained legacy API needs it. For personal details, preserve a per-person privacy boundary; use private Submissions or Personal when their built-in semantics are required.\n4. For the part that can run statically, give them: (a) a one-paragraph explanation of how to structure it, (b) any relevant snippet (storage, routing, external API call), (c) the gotchas.\n5. If they're starting from scratch, finish with a \"ready to deploy\" handoff: tell them to use the `website-deploy` skill, which handles registration (only without the connector), framework-aware build, packaging, and upload.\n6. If they want to wire a capability into a site they've already deployed, generate a focused prompt they can paste into a fresh agent chat (in their site's repo). Include the pattern, the storage shape, and any gotcha — nothing else. If the change deletes data, makes private data public or changes who can see or save, the prompt says to confirm that step with the person first.\n\n## Capability tree\n\n### 1. Static hosting (the baseline)\n\nWhat it is: any folder of HTML/CSS/JS/assets served as-is. Build any framework's normal production output (`dist/`, `build/`, `out/`, `public/`, `.output/public/`) and upload.\n\nWhen to choose: every Website Deploy site starts here. Deploy first, then layer storage and external calls.\n\nGotchas: use relative links (`style.css`, not `/style.css`, and `about.html`, not `/about`) so previews and a new site's first minutes work too; root-relative links work only at the live address. For framework builds, set the base/public path so output uses relative URLs (e.g. Vite `base: './'`, Next `basePath` / relative assets, etc.). Don't ship `node_modules/` or `.env`. A site may be up to 300 MB on simple-host.app.\n\nPhotos: resize to what the page shows (about 1600 px on the long side, 800 px for cards and thumbnails) and save as WebP or JPEG at quality 75–80, under ~300 KB each; never camera originals or PNG photos (PNG or SVG is for logos, icons and flat graphics); every deploy keeps a full copy as a version, so small files matter.\n\n### 2. Existing shared JSON state (compatibility)\n\nWhat it is: a single JSON document (up to 1 MB) scoped to your site. The server stores it in Postgres; your site reads and writes it from the browser. The document is shared across **everyone** who visits — use `PATCH` ops so concurrent writers don't clobber each other.\n\nWhen to choose: anything you'd want a tiny key-value store for — a shared note, a counter, a vote tally, content the page generated, configuration. Reading is public. **Writing from a page needs sign-in**: the page loads `https://simple-host.app/auth.js`, sets `window.SH_CONFIG = { site: \"<sitename>\" }` (needed on a custom domain, harmless everywhere), and calls `await SH.requireSignIn()` before each write — the visitor signs in with Google or an emailed code. The session is site-scoped and is not an API key. Sign-in gates writing only — it does not make the page private. If you need per-visitor data, store it under different keys inside the document, keyed on something like `crypto.randomUUID()` saved in `localStorage`.\n\nHow to use, from a page on the site:\n\n```html\n<div id=\"sh-auth\"></div>\n<form id=\"f\"><textarea name=\"draft\"></textarea><button>Save</button></form>\n<p id=\"status\"></p>\n<script>window.SH_CONFIG = { site: \"<sitename>\" };</script>\n<script src=\"https://simple-host.app/auth.js\" defer></script>\n<script>\nwindow.addEventListener('DOMContentLoaded', async function () {\n  SH.mount('#sh-auth');                              // Google sign-in + email-code form\n  const status = document.getElementById('status');\n  const form = document.getElementById('f');\n\n  // load — public, no sign-in\n  const { data } = await SH.state.get();\n  form.draft.value = data.draft || '';\n\n  // save — sign in first, then write; keep the form on failure\n  form.onsubmit = async function (e) {\n    e.preventDefault();\n    await SH.requireSignIn();                        // signs the visitor in if needed\n    try {\n      await SH.state.patch([{ op: 'set', path: 'draft', value: form.draft.value }]);\n      status.textContent = 'Saved';\n    } catch (err) {\n      status.textContent = 'Not saved: ' + (err.code || err.status);   // never claim success\n    }\n  };\n});\n</script>\n```\n\nFull `SH` API (`SH.data(name, kind)`, and on older sites `SH.state` and `SH.collection(name)`; `SH.me`, `SH.signOut`) and the error bodies are in the `website-deploy` skill's `references/backend.md`.\n\nGotchas: state is public to anyone with the link; never keep personal details in it (use a private collection). Body cap is 1 MB; sending more returns 413.\n\n### 3. Per-visitor state with `localStorage`\n\nWhat it is: small JSON blobs stored in the visitor's browser, scoped to the page's origin (each site's own `<sitename>.<handle>.simple-host.app`, or its custom domain).\n\nWhen to choose: anything you'd want a tiny key-value store for in a single-visitor experience — drafts, settings, app state, the user's progress. Per-visitor only; there is no sharing across browsers or devices.\n\n```js\n// save\nlocalStorage.setItem('myapp.state', JSON.stringify(state));\n\n// load\nconst raw = localStorage.getItem('myapp.state');\nconst state = raw ? JSON.parse(raw) : {};\n```\n\nGotchas: typical browser quota is ~5 MB per origin. Cleared by the user at any time. Prefix your keys with the site name: on the `<handle>.simple-host.app/<sitename>/` fallback address a person's sites share one origin. For multi-megabyte structured data, use `IndexedDB` instead.\n\n### 4. Larger per-visitor state with `IndexedDB`\n\nWhen `localStorage`'s ~5 MB cap is too small or you have a lot of small records, use `IndexedDB` directly. It is built into every browser, so no library or CDN import is needed.\n\n```js\nfunction openDB() {\n  return new Promise((resolve, reject) => {\n    const req = indexedDB.open('myapp', 1);\n    req.onupgradeneeded = () => req.result.createObjectStore('items', { keyPath: 'id' });\n    req.onsuccess = () => resolve(req.result);\n    req.onerror = () => reject(req.error);\n  });\n}\nfunction run(db, mode, fn) {\n  return new Promise((resolve, reject) => {\n    const tx = db.transaction('items', mode);\n    const req = fn(tx.objectStore('items'));\n    tx.oncomplete = () => resolve(req.result);\n    tx.onerror = () => reject(tx.error);\n  });\n}\nconst db = await openDB();\nawait run(db, 'readwrite', s => s.put({ id: 'a', text: 'hello' }));\nconst item = await run(db, 'readonly', s => s.get('a'));\n```\n\nGotchas: same per-origin / per-visitor scoping as `localStorage`. Cleared if the user clears site data.\n\n### 5. Calling external APIs from the browser\n\nWhat it is: `fetch()` from your page directly to any public HTTPS API that returns CORS-friendly responses.\n\nWhen to choose: pulling in public data (weather, Wikipedia, public LLM APIs the user provides their own key for, etc.).\n\n```js\nconst r = await fetch('https://api.example.com/v1/things');\nconst data = await r.json();\n```\n\nGotchas:\n- **CORS** — the upstream API must include `Access-Control-Allow-Origin`. If it doesn't, the browser blocks the response and there's nothing Website Deploy can do; you need a server-side proxy that you control elsewhere.\n- **Keys** — anything in your client-side code is visible to anyone who opens DevTools. Don't bake in API keys. If the API requires a key, have the user paste it into a small input field and save it to `localStorage` with a \"paste a fresh key\" hint when it's missing.\n- **Rate limits** — public APIs throttle by IP. If your site is on a shared machine, that quota is shared too.\n\n### 6. Static reports from generated exports\n\nWhat it is: a static HTML/JS dashboard or report built from data that was exported before deploy. The export becomes ordinary site data (`.json`, `.csv`, or pre-rendered HTML) and Website Deploy only serves the finished files.\n\nWhen to choose: public reports, class projects, research notes, and read-only dashboards where the private work already happened in another tool. For example, a user can turn a sanitized analytics or social export (a follower list, a search result, a CSV of metrics) into a static report, then deploy the report output here.\n\nGotchas:\n- Every deployed file is public. Remove keys, cookies, and anything the user did not explicitly approve for publication.\n- Prefer small, pre-filtered exports. Large raw datasets can exceed archive limits and make the page slow.\n- Do not fetch private APIs from the browser unless the user supplies a key at runtime. If the report needs server-side refresh, Website Deploy is only the static front-end, not the refresh worker.\n\n### 7. Routing patterns\n\nWebsite Deploy serves files. There is no rewrite layer. Because sites live under `/<sitename>/`, keep links **relative** so navigation stays inside the site path.\n\n- **Multi-page static site**: every page is a real `index.html` under a directory. `about/` resolves to `about/index.html` under the site path.\n- **SPA with framework router**: build for static export (see the `website-deploy` skill's framework section) **with a relative base**. Use the framework's hash-router mode or generate a `404.html` that bootstraps the app.\n- **Pretty URLs for plain HTML**: put each \"page\" in its own folder with an `index.html` (`about/index.html`, `pricing/index.html`).\n\n### 8. A nicer address: free name or custom domain\n\nOptional — every site already has its own `https://<sitename>.<handle>.simple-host.app/`, where sign-in and private collections work. The quickest nicer address is a free `<name>.simple-host.app`: `connect_domain` (or `POST /v1/sites/<sitename>/domain`) with `{\"domain\":\"clay-studio.simple-host.app\"}` answers `active` at once, no DNS. First come, first served. A user can instead serve a site from their own domain (e.g. `recipes.brand.com`) — use the `connect-domain` skill (`simple-host-website/skills/connect-domain`). Summary: `POST /v1/sites/<sitename>/domain` with `{domain}` → user adds two DNS records (the address and a TXT ownership record) → poll `GET /v1/sites/<sitename>/domain` until `active` (with the connector: `connect_domain`, then `domain_status`). Either one changes the address; sign-in and private collections carry over. Pages stay public. Once connected, the site lives only at that address: its `<sitename>.<handle>.simple-host.app` URL 302s there and takes no writes for it (agents keep writing through the apex `https://simple-host.app/v1/...`).\n\n## Picking a capability mix\n\n| User says | Capabilities |\n|---|---|\n| \"a landing page / portfolio / CV\" | static only |\n| \"only my family / class / team should see it\" | static + a site passcode (`set_site_passcode`; they share it themselves) |\n| \"a guestbook\" | static + a KV namespace or SQLite table; choose read/write policy and fields for this guestbook. An existing guestbook using public Submissions can keep them. |\n| \"a waitlist / event RSVP / signup form\" | static + private Submissions if each visitor must see, edit or withdraw only their own entry; a whole-resource SQL/KV policy alone cannot do that. |\n| \"take orders / bookings / a survey\" | private Submissions when visitor-specific privacy is needed; optionally an owner-only SQLite resource for separate owner-managed workflow data. |\n| \"a poll / a vote\" | SQLite for the tally and app-chosen vote schema only if its whole-resource policy and duplicate-vote rules fit; retain `one_per_person` Submissions when that built-in guarantee is needed. |\n| \"a menu / opening hours / prices I update\" | static + owner-write, anyone-read KV resource; retain Page info on a site already using its history. |\n| \"a habit tracker / saved progress / my reading list, on any device\" | Personal (`kind: mine`) when each account needs a private record; a signed-in KV/SQLite resource would expose all visitors' records. |\n| \"a shared shopping list / kanban / potluck sign-up\" | static + SQLite table with app-chosen columns, or KV keys; choose independent read/write policies. Existing Shared boards keep their item versions and undo. |\n| \"a tool that runs entirely in the browser\" (calculator, drawing app, game) | static + `localStorage` for settings/saves |\n| \"a journal / notes app\" | static + `IndexedDB` (single-visitor scope) |\n| \"a dashboard pulling from a public API\" | static + external `fetch()` |\n| \"a report from an exported dataset (analytics, social, etc.)\" | static export + optional client-side filtering |\n| \"a multi-page site\" | static only — each page is its own folder + `index.html` (relative links) |\n| \"my own domain / brand.com\" | static + `connect-domain` skill |\n| \"a shorter address, but no domain\" | static + free `<name>.simple-host.app` (one `connect_domain` call) |\n| \"every site of mine under my domain\" (`<site>.trips.brand.com`) | an address family: `connect-domain` skill §Many sites under one name (`*.trips.brand.com`, for the whole account; ask first) |\n| \"a slide deck I want to share a link to\" | build with Slidev, Reveal.js, or similar and deploy the output |\n\nIf the user needs server-side application code, platform-enforced per-row roles or\ncustom account systems, explain that those pieces need another service. Simple\nHost does provide a per-site SQLite resource; its `read`/`write` policy covers\nthe whole database, not each row. A site passcode is shared with anyone given\nit, while a storage resource may separately inherit or bypass that gate. A\nsigned-in resource is available to every signed-in visitor; do not describe it\nas a private page or per-person data store. Existing private Submissions and\nPersonal records keep their narrower visibility rules.\n\n## Generating a prompt for another agent\n\nWhen the user wants to wire a capability into an existing site, generate a focused prompt to paste into a fresh agent chat. Keep it short.\n\nExample prompt for \"save drafts in localStorage\":\n\n> Add draft autosave to this site. On every change to the text input, write `{text, updatedAt}` to `localStorage['mysite.draft']`. On page load, restore the input value from that key if present. Show a small \"Draft saved\" indicator that fades out after 1 second when the save runs. No external dependencies. Use relative asset links only (a site can also be served under a path).\n\nExample prompt for **maintaining an existing** declared-data guestbook (entries belong to signed-in visitors; this site also has a custom domain, so `SH_CONFIG` is required):\n\n> Add a guestbook to this site (deployed on simple-host, custom domain `guests.example.com`, sitename `guestbook`). First declare the data: `declare_data` with name `entries`, kind `entries`, visibility `public`. Load `https://simple-host.app/auth.js` with `window.SH_CONFIG = { site: \"guestbook\" }` set before the tag, mount `SH.mount('#sh-auth')` next to the form, and call `await SH.requireSignIn()` before `SH.data('entries', 'entries').add({name, message})`. On a non-2xx keep the form and show \"Not saved\". Add `admin.html` (noindex) that lists the collection newest-first. Relative asset links only.\n\nMirror this shape for `IndexedDB`, external API calls, routing, etc.\n\n## Handoff: deploy\n\nOnce the user has decided what to build, they need to deploy. Tell them to use the `website-deploy` skill, which handles registration (only when the Simple Host connector is not available), framework-aware build (with a relative base path), packaging, and upload. Before a new site goes online for the first time, it asks the person once (name, address, public to anyone with the link). The site will be live at `https://<sitename>.<handle>.simple-host.app/` (give them the `site_url` the deploy returned). If they want a nicer address, offer the free `<name>.simple-host.app` or the `connect-domain` skill for their own domain; it is optional.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}