Sites
A repository can have a website. You deploy a directory of files,
such as a build’s output, and knotless serves it at
https://<owner>.knotless.page/<repo>/. The files are stored as blobs
in your own account, in one record that each deploy replaces.
Pages explains how sites are stored and served, and
the command reference lists every flag of
knotless repo site.
Deploy a site
Section titled “Deploy a site”As the repository’s owner, give knotless repo site the repository and the
directory to deploy:
knotless repo site knotless://alice.example.com/blog distIt uploads only the files the live site does not hold already, then replaces the site’s record. It prints how many it uploaded and the address:
uploaded 3 of 41 fileshttps://alice-example-com.knotless.page/blog/The new site is live within seconds. A visitor sees the old site or the new one, never a mix of the two.
The subdomain is your handle with each - doubled and each . turned
into a -: alice.example.com is alice-example-com. It follows your
handle, so it changes when your handle does.
A site holds 1 to 2,000 files.
Deploy a private site
Section titled “Deploy a private site”A private repository’s site is deployed the same way. It is kept in the repository’s space, and only its members can read it. It is served at a host of its own, named by the repository’s key and your plc id:
uploaded 3 of 41 fileshttps://3lbqz7ywmz22k-abcdefghijklmnopqrstuvwx.private.knotless.page/The address stays the same when the repository is renamed. The site is
served at the host’s root, so build it with / as its base path.
A member who opens it is sent to knotless.dev to log in, if they are not already logged in, and comes back. Someone who is not a member gets a 404. A member you remove stops reading the site within a minute.
Build for the path
Section titled “Build for the path”The site is served under /<repo>/, not at the root, so build it with
that as its base path. In Vite, that is base:
// vite.config.jsexport default { base: '/blog/' }In SvelteKit it is kit.paths.base. A build with the wrong base loads
its pages but not their scripts and styles.
Serve a single-page app
Section titled “Serve a single-page app”A single-page app routes in the browser, so every page has to load the app. Name the app’s page as the fallback:
knotless repo site knotless://alice.example.com/app dist --fallback index.htmlA request for a page that no file matches then gets index.html, with
status 200, and the app’s router takes the path from there. A request
counts as one for a page when it accepts text/html, or when the last
part of its path has no extension.
A file still comes first: /app/assets/main.js serves that file. A
missing script or image gets a 404, not the app’s HTML, so a broken build
shows as one.
Add a 404 page
Section titled “Add a 404 page”To answer a missing path with a page of your own, name it:
knotless repo site knotless://alice.example.com/blog dist --not-found 404.htmlA path that no file matches gets that page with status 404. With a fallback too, pages get the fallback, and other files get the 404 page.
The fallback and the 404 page must each be one of the files you deploy.
Deploy on every push
Section titled “Deploy on every push”The site shared step deploys from a workflow. It runs knotless repo site as
the repository’s owner, and installs knotless first if the image has
none.
The step logs in with an app password of the owner’s account. Make one at your PDS (on Bluesky, under Settings, Privacy and security, App passwords). Then give it to the runners as a secret, limited to the branch you deploy from:
printf %s "$APP_PASSWORD" | knotless secret knotless://alice.example.com/blog set SITE_PASSWORD --ref refs/heads/mainThen add a workflow that builds the site and deploys it, on a push to that branch only:
# .knotless/workflows/site.ymlon: push: {refs: [refs/heads/main]}image: node:24secrets: [SITE_PASSWORD]steps: - name: build run: npm ci && npm run build - uses: chadtmiller.com/steps/site@1 with: {dir: dist, fallback: index.html} secrets: [SITE_PASSWORD]The step’s inputs are dir, the directory to deploy, and optionally
fallback, not-found, repo (this run’s repository if not given) and
appview. Pin it before you push, and commit the lock file:
knotless uses updategit add .knotless/uses.lockEach deploy records the commit it was built from. Shared
steps explains how uses works,
and Workflows how to run a runner and give it secrets.
See what is live
Section titled “See what is live”With no directory, knotless repo site shows the live site: its address, how
many files it holds, the commit it was built from, and when it was
deployed:
knotless repo site knotless://alice.example.com/blogThe repository’s page shows the site’s address too, under its description. You don’t need to add the address to the repository’s links. A private repository’s site is shown only to its members.
There is no history of deploys. To roll back, deploy an older commit’s build again.
Take a site down
Section titled “Take a site down”knotless repo site knotless://alice.example.com/blog --downThis deletes the site’s record, and the site stops being served.