nsite-clay

A self-editable nsite.
One HTML file that edits and republishes itself, hosted on Nostr.

Homepage · Live demo · Runtime reference

The homepage is itself an nsite-clay document, and it edits itself.
It is served at nsite-clay.cypherpunk.today for readability, which redirects to the address that actually owns it:
npub12edc7326qsryw5rw5yw0yh57fmj9r8jf4c8xazz6333w305qgnms9ypvj2.nsite.lol. That one needs no domain, no server and no renewal.

The heading of a published page being edited in the browser, with the block menu open

--- ### More from me **Related projects** - [nostr-emanator](https://github.com/jooray/nostr-emanator): schedule Nostr posts, paired over NIP-46 and Amber - [nostrautica](https://github.com/jooray/nostrautica): Nostr-native event organizer with end-to-end encrypted data - [oracolo](https://github.com/jooray/oracolo): a Nostr blog in a single HTML file - [anonmicroblog](https://github.com/jooray/anonmicroblog): anonymous microblogs on Nostr - [lievik](https://github.com/jooray/lievik): Nostr-first content curation for creators with several audiences **Full project showcase:** [nsite-clay in my project showcase](https://juraj.bednar.io/showcase/#PUB-01), or [all my projects](https://juraj.bednar.io/showcase/). I write about building things on [my blog](https://juraj.bednar.io/en/blog-en/). I also wrote a cypherpunk novel, [Tamers of Entropy](https://tamersofentropy.net/), and there is a [trailer](https://tamersofentropy.net/#trailer). Open the page, sign in with your key, and type into it. The document serialises its own DOM, pushes those bytes to a [Blossom](https://github.com/hzrd149/blossom) server as one content-addressed blob, and republishes the [NIP-5A](https://github.com/nostr-protocol/nips/blob/master/5A.md) nsite manifest that points at it. Anyone else loading the page gets a plain static document with your changes already in it. Nothing in that loop is a server you have to run. Relays hold the manifest, Blossom servers hold the bytes, and the browser does the rest. Ownership is a keypair rather than an account, so "who may edit this page" is not a row in somebody else's database. The idea is [HyperClay](https://hyperclay.com)'s and the editing model follows [ClayJS](https://clayjs.com); this is that model with Nostr underneath instead of a hosting platform. ## Description for Nostr geeks An nsite is web hosting. Not long-form notes, whole HTML files. That is all an nsite is. nsite-clay adds one thing to it: the page edits and updates itself, so the CMS sits inside the static HTML rather than behind it. You change the DOM. Click around the page and edit things, or open your browser's DOM inspector and change it there; the page does not care which. The new version is uploaded to your Blossom servers, and one replaceable event is republished saying which Blossom hash `/` or `/about-us` now points to. Any nsite gateway renders it. Saving keeps the original HTML's formatting wherever it can. A small text edit does not have to rewrite every quote and space around it. The runtime checks that the saved markup still represents the page and falls back to a full serialization if it cannot preserve the source safely. ## Write and edit with AI Describe the page you want in [the publisher](https://nsite-clay.cypherpunk.today/deploy.html) and AI writes it: the layout, the stylesheet, text you can edit and empty frames for your own photos. Look at the preview, ask for changes, and publish it with your own key. On a page that is already published, press **Edit with AI** and say what should change, either the whole page or only the part you clicked. Nothing reaches the page until you keep the result. **Undo** takes it back, and **Save** publishes it. You pay for AI in one of three ways: 1. **Support the project.** The default. Pay one 2000 sat Lightning invoice and everything works: 1000 sats fund nsite-clay, and 1000 become your AI credit on our Routstr node, `routstr.cypherpunk.today`. The credit pays for the page and for the pictures AI makes for it, about 24 sats each. The key is kept encrypted on your own Nostr relays, so it follows you to every device you sign in on, and credit you do not spend can be withdrawn. 2. **Your own Routstr node.** Point **AI settings** at any other Routstr node and add credit there with Lightning or Cashu. The fees go to whoever runs that node. 3. **Bring your own key.** Use any OpenAI-compatible API that accepts requests from a browser, with your own key and the model you choose. What you pay stays between you and that provider. **AI is an extra, and everything else stays free.** The editor, the twelve templates, the block composer and publishing never ask for payment. Pick a template, click into the page and type, exactly as before. The details of each mode are under [AI provider settings](#ai-provider-settings). ## Try it A live document is published under a throwaway key, and the key is public on purpose: **** ``` nsec1064etpv2gs3ttywm7w5enrqdssdg6dawz9fxz0vs34ac545l6jfqk3987y ``` Open it, press **Sign in**, paste that nsec, and edit the page. Your save rewrites the real document for everyone. It is a sandbox with no owner, so treat whatever is there as other people's leftovers. ## Quick start, without a terminal Open **[the publisher](https://nsite-clay.cypherpunk.today/deploy.html)**, pick a template, sign in with your key or make one on the spot, and it is online. You can also describe a page for AI to build in the second step. Describe what you want, generate a preview, and review it before choosing an address and publishing. The generated page has editable text, a content form and blocks you can add later. The publisher supplies its owner and runtime files. It runs entirely in the browser: it fetches the template and the runtime from the site it is published on, uploads them to your Blossom servers, and signs the manifest with your key. There is no server in the loop and nothing to install. The [guide](https://nsite-clay.cypherpunk.today/guide.html) walks the whole path with screenshots, from having no key at all to a published page with a picture and a Nostr feed in it. ## Quick start, with one ```bash npx nsite-clay init mysite # scaffolds index.html + nsite-clay.js, generates a key npx nsite-clay deploy mysite --sec=nsec1… ``` `deploy` prints the URL. Open it, sign in as the owner, and from then on the page saves itself; you only come back to the CLI for changes you make outside the browser. To use a key you already have, hand `init` the npub so the document knows its owner: ```bash npx nsite-clay init mysite --npub=npub1… npx nsite-clay deploy mysite --bunker="bunker://…" ``` ## What a document looks like ```html Notes

My notes

Type anything here.

``` That is the whole integration. `nc:owner` says whose signature may save it, `editable` marks what a person can type into, and the script does the rest. Save it, then open the file in a text editor: your words are in the HTML. ## Editing Mark a container `editable` and it becomes rich text for the owner and ordinary markup for everybody else. **Enter starts a new paragraph.** Selecting text raises a floating toolbar whose first control is a block menu: Paragraph, Heading 1 to 3, Quote, Code block. Alongside it are bold, italic, strikethrough, bulleted and numbered lists, link, and clear formatting. Keyboard: `⌘B` `⌘I` `⌘U` `⌘K`, `⌘⇧8` for a bulleted list, `⌥1` to `⌥3` for headings. Typing `# `, `- ` or `> ` at the start of a line works too. Use `editable="single-line"` for a title or a list row, where Enter should do nothing and the block menu makes no sense. What reaches the file is markup a person could have written by hand. The `editable` attribute stays behind as an inert marker; the `contenteditable` it implies, the toolbar, and the debris browsers emit from editing commands are all stripped. Pasted markup goes through a sanitiser, so copying out of a word processor does not smuggle a stylesheet into your document. Undo and Redo on the page toolbar cover text edits, CMS fields and changes to blocks. `⌘Z` / `Ctrl+Z` undoes a page edit; add Shift to redo it. Deleted blocks return with their event handlers intact. This history lasts for the current editing session; History still opens your published versions. The content form can use dropdowns, numbers, dates and rich text. Pages declare the field type next to the content, so a price can reject a negative number and a status can offer the choices the page actually uses. Scripts can read and update the same fields through `nc.cms.getData()` and `nc.cms.setData()`. ## AI provider settings Choose **Edit with AI** to change the whole static page. To change one part, click its text or block first, then choose **Only what I clicked** in the dialog. A whole-page rewrite keeps the page's live feeds exactly as they were and may move them; a result that leaves one out is refused. Pages with forms or custom scripts support selected-text AI edits only, because the static page builder cannot preserve those behaviours in a whole-page rewrite. Describe the change and review the preview. **Keep this change** applies it as one undo step; **Save** publishes it. **Back to my instruction** keeps your prompt so you can revise it. **Ask for another change** refines the proposed version; the version selector lets you return to an earlier proposal, and **Show current page** compares it with the unchanged page. Up to five proposed versions stay in this browser, under your key and this page's path. Reopen **Edit with AI** after reloading to resume. Download a draft or restore its JSON file to move it between browsers. Cancelling leaves the page alone; keeping or discarding the draft clears its saved copy. Open **Settings → AI settings** to choose a provider and model. The default is DeepSeek V4.1 Flash through `routstr.cypherpunk.today`, our Routstr node. Its fees help fund nsite-clay. Requests stay on that node unless you choose another. **Add AI credit** opens a separate amount-and-payment dialog for Lightning or Cashu, then returns you to your page. Before generation, the editor checks available Routstr credit and shows a rough price range from current model rates. Unknown balances are labelled unknown, and BYOK balances stay with that provider. AI settings keeps provider details under **Advanced provider settings**, backup controls under **Backup and recovery**, and withdrawal in its own dialog. Saving settings or receiving credit backs up the endpoint, provider type, model and key to your encrypted Nostr vault. A failed backup is shown with recovery instructions. The setup is separate from your published page. For model comparisons, see [the AI evaluation prompts and runner](docs/ai-evaluation.md). You can also enter another Routstr node or bring a key for an OpenAI-compatible API. That endpoint must allow browser requests. Changing nodes keeps their keys separate; it does not send the old node's key to the new one. ## Pictures, video and Nostr posts Three more buttons on the same toolbar. **Pictures.** Drag a file in, pick one you have uploaded before from a grid, or paste a URL. Uploads go to the same Blossom servers the document is stored on, so an image is content-addressed and its URL never has to change. Choose a crop before uploading a new file: free, square, wide or 4:3. The crop happens in your browser; only the accepted image is uploaded. Keep the original for SVGs or animated pictures you want to preserve. **Video.** A YouTube or Vimeo link becomes a click-to-play thumbnail rather than an iframe, so nothing third-party loads until a reader presses play and the thumbnail still works as a link with JavaScript off. A video file is uploaded and played from Blossom. **Nostr feeds.** A widget showing short notes, long-form articles or picture posts from any npubs you name. The dialog lists their actual posts so you can click the ones to pin rather than hunting for event ids. Pinning an article uses its slug, not its event id, because kind 30023 is replaceable and an id-pin breaks the moment the author fixes a typo. Every event is signature-checked and sanitised in the browser, and the posts are fetched at view time rather than stored in the file. ```html
``` ## Writing Nostr posts The composer lists what you have published and opens an editor for more: short notes (kind 1) and long-form articles (kind 30023). Publishing an article again under a slug you have used before is the edit, so every long-form client shows the new text at the same address and the original date stays put. Posts and pages are separate. Writing a note does not change your page, and saving your page does not touch your posts. ## Blocks A page marked `nc:blocks` is assembled rather than typed over. In edit mode the runtime draws a rail on every block (move it, duplicate it, delete it, list what it is made of, and for a picture or a feed a gear that reopens the picker) and an insert point between them that opens a palette. The list is the way inside a block. A block is markup, and markup accumulates: a duplicated card, an element some editing command split in two, an empty box left behind by something that went wrong. Without a way in, the only cure for one stray element is to delete the block around it and build the whole thing again. The palette is built from the document's own library, which is a set of inert `