johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
# 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](../design/pages.md) explains how sites are stored and served, and
the [command reference](../reference/cli.md) lists every flag of
`knotless repo site`.
## Deploy a site
As the repository's owner, give `knotless repo site` the repository and the
directory to deploy:
```sh
knotless repo site knotless://alice.example.com/blog dist
```
It 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 files
https://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
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 files
https://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
The site is served under `/<repo>/`, not at the root, so build it with
that as its base path. In Vite, that is `base`:
```js
// vite.config.js
export 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
A single-page app routes in the browser, so every page has to load the
app. Name the app's page as the fallback:
```sh
knotless repo site knotless://alice.example.com/app dist --fallback index.html
```
A 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
To answer a missing path with a page of your own, name it:
```sh
knotless repo site knotless://alice.example.com/blog dist --not-found 404.html
```
A 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
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:
```sh
printf %s "$APP_PASSWORD" | knotless secret knotless://alice.example.com/blog set SITE_PASSWORD --ref refs/heads/main
```
Then add a workflow that builds the site and deploys it, on a push to
that branch only:
```yaml
# .knotless/workflows/site.yml
on:
push: {refs: [refs/heads/main]}
image: node:24
secrets: [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:
```sh
knotless uses update
git add .knotless/uses.lock
```
Each deploy records the commit it was built from. [Shared
steps](../reference/workflows.md#shared-steps) explains how `uses` works,
and [Workflows](workflows.md) how to run a runner and give it secrets.
## 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:
```sh
knotless repo site knotless://alice.example.com/blog
```
The 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
```sh
knotless repo site knotless://alice.example.com/blog --down
```
This deletes the site's record, and the site stops being served.