Kitten

Reference

The reference section is under development and should in no way, shape, or form, be considered to be exhaustive at the moment.

This reference guide lists the features of Kitten. For a comprehensive “owner’s manual”-style technical reference of how Kitten is designed and works internally, please see the Technical Manual.

About

Kitten is a little kit (“kitten”, get it?) optimised for people (not corporations) who make the new everyday things for themselves and other people – not because they want to become billionaires but because they want to contribute to the common good by helping nurture the Small Web while adhering to the Small Technology Principles.

Kitten features intuitive file system-based routing, HTML and accessibility validation, a built-in object database, WebSockets, zero-code authentication, public-key cryptography, Markdown support, syntax highlighting via highlight.js, semantic styles via Water CSS, and more.

System requirements

macOS comes with an ancient version of Bash. To upgrade it to a modern one, run brew install bash using Homebrew.

For production servers, only Linux with systemd is supported.

Installing Kitten

To install Kitten, either:

Updating Kitten

You can update Kitten in a variety of ways:

Authoring

HTML

You work with HTML in Kitten using Kitten’s html tagged template string format that is exposed at kitten.html.

e.g., Here’s a simple page route that prints “Hello, world!”:

export default function () {
  return kitten.html`
    <h1>Hello, world!</h1>
  `
}

Put the above code into a file called index.page.js, run kitten in that directory, and navigate to https://localhost in your browser to see it work.

To learn more about Kitten HTML, see the Dynamic Pages tutorial.

Kitten has a semi-forgiving HTML parser.

The page tag

You’ll notice in the Hello, world! example that we just returned a <h1> tag instead of the full HTML structure for the page.

Kitten has a default template that creates the boilerplate of an HTML page for you (the whole structure, starting with <!doctype html><html lang='…'><head>…, etc.)

To add to parts of this template you can use two mechanisms:

  1. The <page> tag
  2. Special page slots

Special page slots take precedence over and override <page> tag settings.

The page tag takes the following attributes:

HTML attributes

These customise the <html> tag:

Head attributes

These customise the <head> tag:

Body attributes

These customise the <body> tag:

Libray includes

These attributes trigger library includes:

Libraries like htmx, idiomorph, etc., are automatically added to the page when you use the connect attribute on Kitten components, export event handlers from the your Kitten pages or make your pages into KittenPage subclasses. In other words, in daily usage, you should not have to specify the htmx, idiomorph, or ws tags manually.

Other attributes

These attributes customise other Kitten-specific behaviour:

Request and response helpers

Kitten has a number of request and response helpers defined to make your life easier.

You use two of them, response.forbidden() and response.get() in the authentication tutorial.

Request

Response

Streaming HTML

Kitten gives you a simple-to-use event-based HTML over WebSocket implementation called Streaming HTML (because you’re streaming HTML updates to the client) that you can use to build web apps.

See the Streaming HTML tutorial for details.

Kitten components

Simple components

A Kitten component is any function that returns kitten.html.

So this can be as simple as:

const SayHello = () => kitten.html`
  <h1>Hello</h1>
`

Of course, static components are not very useful, so component functions usually take a parameter object with properties (or “props”, for short):

const SayAnything = ({ thingToSay = 'Hello' } = {}) => kitten.html`
  <h1>${thingToSay}</h1>
`

console.log(kitten.html`<${SayAnything} thingToSay='Goodbye' />`

The above example, for example, will output:

<h1>Goodbye</h1>

To learn more about Kitten components, see the following tutorials:

kitten.Component components

While simple components are great, if you use Kitten’s Streaming HTML workflow, you may find yourself having to do a bit of manual work if you want to use simple components and wire them up to listen for events from connected pages. This will become especially true if you carry out precise updates, morphing small bits of your interface in response to events and do so in a maintainable manner by breaking up your interface into small, well-encapsulated components.

To make this easier for you, Kitten provides the Component class that you can reference in your apps from the global kitten namespace.

Remember that you can get language intelligence and strong typing for your apps by importing the type safe version of the kitten global from the @small-web/kitten module.

For a real-world example of the use of the kitten.Component class – and its page-level equivalent, kitten.Page – see the code for the Internal Database page of Kitten’s of own Settings app.

Icons

Screenshot of the all icons example at the end of this section showing the icons sorted alphabetically and rendered in duotone deep pink. The icons starting with a and b are visible in the browser.
They’re icons and they’re pink… view source.

Kitten includes a subset of the Phosphor icon set by Helena Zhang and Tobias Fried as Kitten components.

The main difference with the original set is that some icons have been removed and some have been renamed to bring the library in line with the Small Technology Principles.

Specifically:

Finding icons

In your Kitten app, you can access the icons as simple Kitten components at the following namespace:

kitten.icons

In that namespace, you browse the icons in three ways:

The categories and tags objects are authoring-time aids. While you can use them in your code, it’s better practice to reference the icons directly from the root namespace. These two properties are marked as unenumerable so they do not pollute the namespace and will not show up if you’re iterating over all the icons in the root namespace.

To get autocompletion in your editor, your editor must support the TypeScript language server (any modern code editor worth using will e.g., Helix editor, etc.) and you must install and use the type-safe Kitten globals module:

import kitten from '@small-web/kitten'

kitten.icons.categories. // Will show a pop-up with list of categories in your editor.

Using icon components

You can use the components in your kitten.html as you would any other Kitten component.

e.g., A simple Kitten page that displays a large, pink cat icon in duotone:

export default function () {
  return kitten.html`
    <${kitten.icons.Cat}
      size=40%
      weight=duotone
      colour=deeppink
    />
  `
}

As the icons are Kitten components, you can customise how they appear by passing properties (“props”) to them:

And other props you provide will be passed as attributes to the SVG tag of the icon.

Extending icon components

You can extend the SVG (e.g., to add animation, etc.) by slotting any valid SVG content into it.

e.g., This makes the cat into a Cheshire cat (fades in and out):

export default function () {
  return kitten.html`
    <${kitten.icons.Cat}
      size=40%
      weight=duotone
      colour=deeppink
    >
      <animate
        attributeName="opacity"
        values="0;1;0"
        dur="3s"
        repeatCount="indefinite"
      />
    </>
  `
}

While the above example is good for a one-time use modification, you can, of course also make a reusable CheshireCat component.

Here’s one that has all the same defaults as the one in the above example but is fully configurable via props.

Notice how it passes any other props it receives to the base component it’s extending via composition.

const CheshireCat = ({duration = 3, minOpacity = 0, maxOpacity = 1, ...props}) => {
  return kitten.html`
    <${kitten.icons.Cat} ...${props}>
      <animate
        attributeName="opacity"
        values="${minOpacity};${maxOpacity};${minOpacity}"
        dur="${duration}s"
        repeatCount="indefinite"
      />
    </>
  `
}

export default function () {
  return kitten.html`
    <${CheshireCat}
      size=40%
      weight=duotone
      colour=deeppink
    />
  `
}

Displaying all icons

Here’s a simple example that displays all the icons, separated alphabetically, with their component names appearing in a tooltip when you hover over them.

You can find a screenshot of it at the top of this section.

export default function () {
  return kitten.html`
    <page css>
    <markdown>
      # All icons

      These are all the icons in the [code]kitten.icons[code] namespace.
    </markdown>
    ${Object.entries(kitten.icons).map(([name, Icon]) => kitten.html`
      <${Icon} size=2em weight=duotone colour=deeppink>
        <title>${name}</title>
      </>
    `)}
  `
}

Markdown support

Kitten’s Markdown support comes from markdown-it.

Kitten supports a very rich set of Markdown features including:

You can use Markdown in your Kitten apps in a variety of ways:

Markdown pages (.page.md files).

You can create whole pages using Markdown by placing your Markdown in page.md files. These pages are transpiled into JavaScript before being transpiled into HTML just like regular JavaScript pages (page.js) files.

Your Markdown pages can have front matter in YAML format.

Kitten’s YAML parser is based on the Node yaml module and you can use it directly in your own Kitten apps by referencing it from kitten.yaml.

Front matter

Kitten’s YAML front matter supports some special properties. Specifically:

layout (string)

Specifies the layout template to use for the Markdown page.

The value for the layout property is a string that contains the relative path to the layout template (.layout.js file) you want to import and use.

Any custom properties in front matter are passed to the layout template as properties.

For example, if your layout template is called Main.layout.js and it is located in the layouts/ folder of your project and you want to include it in a page called index.page.md at the root of your project, your front matter might look like this:

---
layout: './layouts/Main.layout.js'
title: 'Feed me!'
author: 'Oskar Kalbag'
---

Woof.

And your layout template might look like this:

export default function MainLayout ({ SLOT, title, author } = {}) {
  return kitten.html`
    <header>
      <h1>Oskar’s blog</h1>
      <p>I am a dog.</p>
    </header>
    
    <main>
      <h2>${title} by ${author}</h2>
      ${SLOT}
    </main>

    <footer>
      <p>PS. Have you fed me?</p>
    </footer>
  `
}

The title and author fields you see in the example above are custom properties the author wants passed to the layout template.

import (list)

You can import components and fragments and use them in your Markdown pages just as you would in regular Kitten pages.

The import list contains JavaScript import statements in the following form:

---
imports:
  - import SomeDefaultExport from '../path/to/ComponentName.component.js'
  - import { ANamedExport, AndAnother, AndThisOne as ThisOtherThing } from '../path/to/SomeComponents.component.js'
---

Once imported as shown above, you can use then using them in your Markdown page as you would normally:

# Components and fragments

This is a component without slotted content:

<${ANamedExport} prop=someValue anotherProp=someOtherValue />

And here’s one with slotted content:

<${ThisOtherThing} prop=someValue anotherProp=someOtherValue>
  <markdown>
    ## Now, isn’t this fancy?
  </markdown>
</>

kittenPage (true or false; default is false)

Determines whether the resulting page exports a simple page render function (when kittenPage is not provided or when it is set to false) or a Kitten.Page subclass.

Setting kittenPage: true in your front matter will enable you to use the Streaming HTML workflow with any components you attach to your page (as you will be able to author event handlers on them and, due to the Kitten Component hierarchy and automatic event bubbling, their handlers will get called).

Since your markdown pages are markdown and not JavaScript, they cannot export any event handlers of their own.

// LEFT OFF HERE: Update above paragraph after I implement support for this.

Custom properties in front matter

Any properties outside of the special ones listed above are passed to your page’s layout template (if it has one) as props.

In addition to any custom properties you may have, three built-in properties are always passed to the layout template by Kitten:

Markdown fragments

You can place static pieces of Markdown into Markdown fragment files (fragment.md).

Unlike Markdown pages, Markdown fragments do not support front matter or template substitution. They are purely for static content.

Markdown in Kitten HTML tagged template strings

Finally, you can embed Markdown directly in your Kitten HTML tagged template string by surrounding your Markdown content between <markdown>…</markdown> tags.

e.g.,

export default () => kitten.html`
<h1>This is HTML</h1>
<markdown>
  - _This_
  - is
  - __Markdown!__
</markdown>
<footer>
  <p>This is more HTML</p>
</footer>
`

Unlike HTML, Markdown is whitespace-sensitive so make sure you indent your code properly or it will get parsed incorrectly.

Syntax highlighting

Syntax highlighting in Markdown is provided via Highlight.js.

The default themes for light and dark mode are the Measured Light and Measured Dark accessible syntax highlighting colour scheme implementations for Highlight.js.

You can override this setting by setting the syntax-highlighter-theme-light and syntax-highlighter-theme-dark attributes of your <page> tag to the absolute or relative path to the respective theme CSS file.View the full list of default Highlight.js themes.

You can see some of the Markdown features mentioned here demonstrated in the examples/components and examples/markdown folders of the Kitten examples, as well as in the source for the Kitten web site itself.

Deploying a Kitten application

To deploy your app as a service, you use the deploy command:

kitten deploy <httpsGitCloneURL>

For example, the following will create and run a production server of the Persisted Kitten Chat example, as hosted on my personal Codeberg account at my hostname.

kitten deploy https://codeberg.org/aral/persisted-kitten-chat

Kitten will not automatically create an alias for www for you, pass --aliases=www.<hostname> (or the shorthand, --aliases=www) if you want that. You can, of course, also list any other subdomains other that www that you want Kitten to serve your site/app on in addition to the main domain.

If your app uses node modules, Kitten will intelligently call npm install on your project, as well as on any app modules your project might have.

Updating a deployed Kitten application

There are a number of ways you may wish to update your deployed Kitten application, depending on who the audience for your app or site is.

🪝Webhook

You can set up a webhook on your remote Git source code repository that updates the site whenever you perform a certain git action (e.g., push to main).

You can find your webhook URL and webhook secret on the App Settings page of your Kitten site (/🐱/settings/app/).

Your Kitten site expects the webhook secret in either the Authorization header or in the body of a POST request in a field called secret (the body should be application/x-www-form-urlencoded).

e.g., Instructions for Codeberg:

  1. Enter your Webhook URL into the Target URL field.
  2. Set the HTTP method to POST.
  3. Set the POST content type to application/x-www-form-urlencoded.
  4. Leave the Secret field empty.
  5. Set Trigger on to Push events
  6. Set Branch filter to main (or set to a separate branch you want to deploy from)
  7. Enter your Webhook secret into the Authorization header field.
  8. Make sure the Active checkbox is checked.
  9. Press the Add webhook button.

Once the webhook has been successfully added, your site should update whenever you git push to main.

This is a feature that will be useful to developers who are deploying their own sites and apps for their own use as opposed to developers making apps for use by everyday people who use technology as an everyday thing. For the latter use case, please see Versioning, below.

Versioning

If you want to quickly test out a Kitten app that doesn’t have versioning information, use the kitten run command instead.

You version your Small Web apps using git tags:

Tag your Small Web apps in the following format to version them:

<Kitten API Version>.<App Version>

Where:

To tag your app, simply use the git tag command as usual, e.g.,

git tag -s 1.3

The above tag will label your current commit as being compatible with Kitten API Version 1 and having an app version of 3.

If a deployed version of your app is running version 1.2, it will not be updated when you push commits to your main branch, etc. It will also not update the installed version of Kitten if, say, a new Kitten package is released that has an API Version of 2. Kitten will only be upgraded to the latest version when you release a version of your app that’s tagged with support for Kitten API Version 2. e.g.,

git tag -s 2.1

Automatic updates of Small Web places and of Kitten itself follows one of the core tenets of the Small Web which is that Small Web places should not require technical knowledge to maintain.

As such, updates should never a break a Small Web place and require technical intervention to fix.

This means that your Small Web apps must never break backwards compatibility. So, for example, if you have schema changes in your database, you must write migration code to give existing data the shape that the latest version of your code requires, etc.

Kitten production servers periodically and automatically check for updates to both Kitten itself and to the Kitten app that is being served. Whenever there is a breaking change to the Kitten API (e.g., an existing method is removed or how it’s used is changed in such a manner that it could break existing code), the change is documented in the change log and the Kitten API version, which is a monotonically increasing integer, is incremented by one.

Automatic versioning in production

To understand how automatic versioning in production works, imagine that you deploy a new server running a Kitten version that has its API version set to 1. You specify this API version in your git tag as shown above. Whenever Kitten checks for Kitten updates, it ensures that the API version is still 1 before updating Kitten.

Say that after three Kitten updates, Kitten notices that the latest version of Kitten has an API version of 2 but that your app (which Kitten is also periodically checking for updates for from its git repository) is still at API version 1. At that point, Kitten does not install the latest version of Kitten but rather waits for you to test your app with Kitten API version 2 and specify that it is supported by creating a new git tag accordingly.

If there are any other Kitten packages released that support Kitten API version 1 (e.g., bug/security fixes that are backported from the latest versions of Kitten), Kitten will continue to update itself to those versions.

Once Kitten sees that you’ve updated your app to Kitten API version 2, it will go ahead and update itself to the latest package supported by that version.

So, all this to say that Kitten tries its hardest to ensure that its auto-updates system does not break your servers.

If you are using a database in your app, please remember that you are responsible for ensuring that schema changes do not break your app between versions. In other words, you are responsible for implementing database migrations.

To make this easier on yourself, you might want to ensure that the collections and individual data items you persist are custom classes that abstract away the actual objects that are persisted. That way, you can implement granular migrations in your model classes.

Otherwise, you can, of course, also implement global, one-shot migrations in the traditional manner.

Production servers require systemd.

🌲 Evergreen Web (404 → 307)

Kitten has built-in support for the Evergreen Web (404 to 307) technique.

To implement 404 to 307 redirection for your Small Web place in Kitten, simply set domain you want to forward to in your Small Web Settings page which you can reach in your browser at /🐱/settings/.

Let’s say you are deploying your new Small Web place at your own domain (e.g., https://ar.al) but you already have a personal web site or a blog there. You will already have content that other people might have linked to and/or rely on. It would be a shame for all those links to break when you deploy your new site.

The Evergreen Web (404 to 307) technique is very simple:

On your new site, issue a 307 redirect for any paths that result in a 404 (not found) error to the previous version of your site. That way, if the path existed in the previous version of your site, it will be found there and, if it did not, it will result in a 404 error there.

Of course, you’re not limited to one level of indirection. If the previous version of your site replaced an even earlier version, there’s no reason why you cannot add a 404 to 307 rule there also and have a chain of redirects going all the way back to the earliest version of your site.

One of the great advantages of this technique is that you can just leave older versions of your site running (perhaps at different subdomains) and they can all continue to exist using whatever technologies they were using at the time. (You don’t have to take static exports, etc., of the data.)

By using 404 to 307, you will contribute to an evergreen web by not breaking URLs.

To see it in action, run the evergreen-web example.

For more information, see 4042307.org.

Cascading archives

Similar to the Evergreen Web technique, but useful when your old site was static (or if you output a static version of your old site).

Kitten looks for three special folders:

If they exist, it serves their contents as static files from the root of the site.

These routes are added after all other routes.

So if a route cannot be found in the current version of your site, Kitten will search for it in the above archives, in the order shown above.

Valid file types

Kitten doesn’t force you to put different types of routes into predefined folders. Instead, it uses file extensions to know how to handle different routes and other code and assets.

Here is a list of the main file types Kitten handles and how it handles them:

ExtensionTypeBehaviour
.page.jsKitten page (JavaScript)JavaScript route that is compiled into HTML and served in response to a HTTP GET request for the specified path.
.page.mdKitten page (Markdown)Markdown route with optional front matter that supports JavaScript imports and the use of Kitten components and fragments in Markdown that is compiled into HTML and served in response to a HTTP GET request for the specified path.
.post.js, .get.js, .put.js, .head.js, .patch.js, .options.js, .connect.js, .delete.js, .trace.jsHTTP routeServed in response to an HTTP request for the specified method and path.
.socket.jsWebSocket routeServed in response to a WebSocket request for the specified path. Only file type to keep part of its file extension in its route pattern (e.g., the file /my.socket.js exists at the URL fragment /my.socket.)
.component.jsA component file, returns HTMLIgnored by router.
.layout.jsLayout component, returns HTMLIgnored by router.
.fragment.jsA fragment file, returns HTMLIgnored by router.
.fragment.htmlAn HTML fragment fileIgnored by router.
.fragment.cssA CSS fragment fileIgnored by router.
.fragment.mdA Markdown fragment fileIgnored by router.
.script.jsServer-side script fileIgnored by router. Useful for including server-side JavaScript modules. File is not accessible from the client.
.styles.jsServer-side styles fileIgnored by router. Useful for including server-side CSS (in JS). File is not accessible from the client.
Other (.html, .css, .js, .jpg, .gif, etc.)Static filesAny other files in your project apart from the ones listed above are served as static files.

HTTP routes

HTTP data routes are served in response to an HTTP request for the specified method and path.

All HTTP request methods are supported.

You create an HTTP route by create a JavaScript file named with the HTTP request method you want to respond to.

For example, to respond to GET requests at /books, you would create a file named books.get.js in the root of your source folder.

The content of HTTP routes is an ESM module that exports a standard Node route request handler that takes http.IncomingMessage and http.ServerResponse arguments.

For example, your books.get.js route might look like this:

export default ({ request, response }) => {
  const books = kitten.db.books.get()
  response.end(books)
}

URL normalisation

Kitten automatically normalises URLs that do not have a trailing slash when they should to add one.

It does so using a permanent 308 redirect that preserves the method of the request.

This means, for example, that a request to _https://my.site/hello_ will be forwarded to _https://my.site/hello/_.

This is both to help implement canonical paths for your sites/apps and to avoid the unexpected situation of a relative include failing from your page if a slash is not provided in the path. (e.g., if you have an index.js file and an index.html file in a folder called hello and you include the JavaScript file using <script src='./index.js'></script>, that will succeed if the page is hit as /hello/ but fail if it is hit as /hello.)

Sessions and authentication

Kitten automatically manages sessions and authentication for you.

You can add a lock emoji (🔒) to the end of any route to make it an authenticated route.

Authenticated routes are only accessible by the owner of the site after they sign in using their secret.

If an authenticated route is a folder, any child routes also require authentication.

You can also manually check if the owner of the site is signed in by checking if request.session.authenticated is true or not.

Finally, you can manually direct people to sign in and out by redirecting them to the following routes:

Remember that the Small Web – and therefore, Kitten – does not have the concept of users. There is only ever one owner of the site, who is a person. And only this owner can sign into the site. The site might be viewed by countless people over time but they cannot authenticate with it or create accounts on it.

(If you want to build this functionality yourself, for your app, you of course can but it is neither encouraged nor supported. Please see the FAQ for more details.)

The idea is that everyone has their own Small Web site and, when they want to communicate with each other, they do so in a peer-to-peer fashion.

This is the core difference between the Big Web – which is made of up of centralised silos often owned by trillion-dollar and multi-billion-dollar corporations that make their money by farming you for your data – and the Small Web, which is a peer-to-peer web of people who use technology as an everyday tool to communicate with each other and enrich their lives.

To understand the technical architecture of the Small Web better, please see the End-to-end encrypted peer-to-peer Small Web apps tutorial.

To learn more about sessions and authentication, please see the following two tutorials:

Data scopes

In Kitten, you have a number of different scopes, with varying lifetimes, where you can store data.

These are, from most limited and ephemeral to widest and most permanent:

ScopeLifetimeAvailabilityUsageExample
Page storageLife of a single page loaded in a browser window/tabOnly with Streaming HTML workflow (if your page exports an onConnect() handler)Store data on the data object you have on the page property you get in your page route and page onConnect() handlers.`Kitten Kawaii
Session storageCommon to all pages within a client-side browser session (e.g., multiple pages open in tabs in the same browser.On any handler that receives a request propertyStore data on the session object you have on the request property you’re passed in your page route and page onConnect() handlers.Sessions tutorial
Route globalsShares state between all handler calls of a route but is ephemeral (destroyed on server restart)Any routeCreate a file-level variable.Draw Together
GlobalsShares state within your application but is ephemeralAnywhereCreate a global variable (e.g., globalThis.myVariable)n/a
PersistedKept in the app database (persisted to disk; survives server restarts)AnywhereSee Database section, below.Persistence tutorial

Database

Kitten has an integrated JSDB database that’s available from all your routes as db.

JSDB is a transparent, in-memory, streaming write-on-update JavaScript database for the Small Web that persists to a JavaScript transaction log.

Tables in JSDB are simply JavaScript objects or arrays and JSDB writes to plain old JavaScript files.

You can also create type-safe databases. To learn how to do so, read the Database App Modules tutorial.

You can inspect and alter your database from the Kitten’s interactive shell while your app/site is running.

You can also get information about the database and specific tables, delete the whole database and specific tables, and tail specific tables using the command-line interface via the db info [tableName] (or just db [tableName], which is a convenience alias), db delete [tableName] and db tail <tableName> commands.

To access the JavaScript source code of your database, follow the “Open app data folder” link in Kitten’s initial terminal output.

Your databases are located under the main data folder:

~/share/small-tech.org/kitten/data/

Each project gets its own folder in there with a name based on the absolute path to your project on your disk. e.g., A Kitten project is stored in /var/home/aral/projects/my-project will have its database in a folder called var.home.aral.projects.my-project in the main data folder.

Learn more about JSDB.

Route parameters

You can include route parameters in your route paths by separating them with underscores and surrounding the parameter names in square brackets.

For example:

manage_[token]_[domain].socket.js

Will create a WebSocket endpoint at:

/manage/:token/:domain.socket

You can also intersperse path fragments with parameters:

books_[id]_pages_[page].page.js

Will compile the Kitten page and make it available for HTTP GET requests at:

/books/:id/pages/:page

So you can access the route via, say, _https://my.site/books/3/pages/10_.

You can also specify the same routes using folder structures. For example, the following directory structure will result in the same route as above:

my-site
  ╰ books
     ╰ [id]
         ╰ pages
             ╰ [page].page.js

Note that you could also have set the name of the page to index[page].page.js_. Using just [page].page.js for a parameterised index page is a shorthand.

You can decide which strategy to follow based on the structure of your app. If, for example, you could access not just the pages but the references and images of a book, it might make sense to use a folder structure:

my-site
  ╰ books
     ╰ [id]
         ├ pages
         │   ╰ [page].page.js
         ├ references
         │   ╰ [reference].page.js
         ╰ images
             ╰ [image].page.js

You may, or may not find that easier to manage than:

my-site
  ├ books_[id]_pages_[page].page.js
  ├ books_[id]_references_[reference].page.js
  ╰ books_[id]_images_[image].page.js

Kitten leaves the decision up to you.

Optional parameters

To create an optional parameter, prefix your parameter name with optional- inside the square brackets.

For example, the following route:

delete
  ╰ index_[subdomain]_[optional-return-page].page.js

Will match both of the following paths:

The wildcard parameter

Finally, you can also use the special wildcard parameter to match any string:

my-site
 ╰ books_[any].page.js

The about route will match any path that begins with /books/.

Under the hood, Kitten uses Polka for its routing and translates Kitten’s file system-based naming syntax to Polka’s routing syntax.

App Modules

When working on larger projects, you might end up with pages at varying levels within the site, all of which need to use the same global resource (for example, a layout template or a utility class).

In these situations, your import statements might start to get unruly.

Take the following example:

my-site
  ├ site.layout.js 
  ╰ books
     ╰ the-handmaids-tail
         ╰ pages
             ╰ cover.page.js   

Let’s say your cover.page.js uses the site layout template.

Here’s what its import statement would look like:

import Site from '../../../site.layout.js'

export default () => kitten.html`
  <${Site}>
    …
  </>
`

That’s both cumbersome to write and error-prone. (Especially when you consider that different pages at different levels of the hierarchy may want to use the same file. You don’t want to spend your day counting dots.)

Enter App Modules.

App Modules are special local Node modules that exist only in your app (not on npm). They live in the special app_modules folder in your project and Kitten knows to ignore them when calculating its routes from the file structure of your project.

App Modules, like all Node modules, need a package.json file but they can get away with specifying a subset of the information contained in ones for npm packages. And, like all Node modules, they need to be npm installed into your project (but from a local file path instead of from npmjs.org).

Finally, if you have type checking enabled in your projects, you should add an index.d.ts file to your App Modules so your editor doesn’t complain when using the TypeScript Language Server (LSP).

Here’s how you’d make your site layout into an App Module:

  1. Create the app_modules directory and then a directory for your module:

    mkdir -p app_modules/site-layout
    
  2. Add the files your module will need:

    my-site
      ├ app_modules
      │  ╰ site-layout 
      │     ├ site.layout.js
      │     ├ index.d.ts
      │     ╰ package.json
      ╰ books
         ╰ the-handmaids-tail
             ╰ pages
                 ╰ cover.page.js  
    
  3. In your package.json file, you need to specify three properties: name, type, and main:

    {
      "name": "@app/site-layout",
      "type": "module",
      "main": "site.layout.js"
    }
    
  4. Finally, if you have type checking enabled and you want to avoid type errors in your editor, your index.d.ts file should look like this:

    import SiteLayout from './site.layout.js'
    export default SiteLayout
    

And that’s it.

All you need to do then is to install the App Module.

From the root of your project, install your module using its local path:

npm install ./app_modules/site-layout

Finally, from cover.page.js (and any other page, anywhere in your project) you can now import your layout using:

import Site from '@app/site-layout'

That’s much better, isn’t it?

Database app modules

Database app modules are special type of app module that Kitten expects to have the name database.

You are not limited to using the default, untyped database Kitten sets up for you.

The default database, like many of the beautiful defaults in Kitten, exists to make it easy to get started with Kitten and use it to build quick Small Web places, teaching programming, etc.

If, however, you’re designing a Small Web place that you will need to maintain for a long period of time and/or that you are going to collaborate with others on, it would make sense to create your own database and to make it type safe so you get both errors and code completion during development time.

You can create your own custom database using a special type of app module called the database app module.

Like any other app module, your database app module goes in the special app_modules directory in the root of your project. What’s special is that the app module must be called database.

Make sure your database app modules are using JSDB version 6.0.1 or later as previous versions suffer from a possible data corruption/security issue where string keys were not being sanitised in the same way values were during serialisation.

To learn how to create your own database app module for your Small Web project, please read the Database App Module tutorial.

For a real world example, see the database app module in Domain that wraps Kitten’s global JSDB instance with type information and some utility methods.

Static files

You do not have to do anything special to server static files with Kitten. It is, first and foremost, a web server, after all. Any file that’s in your project folder that isn’t a hidden file or in a hidden folder and isn’t a special Kitten file (e.g., .page.js, .socket.js, .post.js, etc.), will be served as a static file.

Reserved routes

Kitten tries to be as minimally invasive into your site or app’s own namespace as possible. That said, there are certain routes that it does reserve. Some of these are part of the Small Web protocol and others are Kitten-specific.

Reserved Kitten-specific routes

Kitten-specific routes are kept in the 🐱 namespace so as not to pollute your app/site’s own URI space.

These includes routes for default functionality that Kitten provides like the sign-in and sign-out routes as well as the settings interface:

Reserved Small Web protocol routes

The protocol is currently under development and evolving rapidly.

The following routes are reserved as part of the Small Web protocol, which is namespaced by the Small Web emoji/logo (💕).

They are used to implement public-key authentication and manage the communication between Small Web instances and the Domain hosts that host them:

The Small Web protocol also defines a separate 🤖 (robot) namespace for the server itself (as opposed to the Small Web namespace which is for the person who owns and controls the Small Web place).

Currently, that only contains the following route which returns the server’s public ED25519 signing key:

(This is used during server-to-server communication to ensure that requests actually originate from the server they purport to.)

Kitten currently does not implement the full Small Web protocol. (The Small Web protocol is, itself, a work in progress.) It is currently up to apps to implement aspects of it in ways that make sense for them.

For example, Place is in the process of implementing the following routes:

In time we’ll see whether or not it makes sense to pull in more base Small Web protocol implementations into Kitten proper.

Kitten client-side libraries

Kitten comes with several client-side libraries which are automatically served at runtime that you can use in your sites/apps.

The page tag provides a high-level interface for including most of them on your pages.

For example, to include htmx, you can add the following page tag to any HTML fragment that ends up on your page:

<page htmx>

You can also include any of them in your sites/apps manually, just like any other client-side script, by knowing their paths:

In /🐱/library/:

Library nameFile namePage tag attribute nameVersionNotes
Kitten cryptography modulecrypto-1.jsn/a (ES module)^1
htmxhtmx-2.jshtmx^2Own fork.
htmx WebSockethtmx-ws-2.jsws^2
htmx idiomorph extensionhtmx-ideomorph-0.3.jsidiomorph^0.3.0
Alpine.jsalpinejs-3.jsalpine^3
Prism (CSS)prism-1.cssn/a^1Used internally to syntax highlight Kitten error messages. Also see built-in Highlight.js syntax highlighter.
Water (CSS)water-2.csswater^2

The libraries also have minified and gzipped version (.min.gz) that are automatically used in production.

The -N notation specifies the major version of the library. Since Kitten sites automatically update, if there is a major version update to a library, it will be adding alongside previous versions so as not to break existing sites. So if htmx 2.x.x comes out, for example, with breaking changes and we start supporting it, it will be added as htmx-2.js. All minor and patch updates will be automatically updated without changing the name of the include.

🐢 Kitten’s interactive shell (REPL)

Introduction

Kitten has an interactive shell (REPL) that extends the default Node.js REPL with Kitten-specific features.

When working with Kitten interactively (i.e., during development), you can launch the shell using the s key from any Kitten instance running in your terminal.

You’ll know you’re in Kitten’s interactive shell when you see the following prompt:

🐱 💬

If you’ve deployed Kitten as a system service (daemon) using systemd, you can use the kitten shell command to connect to the daemon over a socket connection.

Commands

In addition to executing JavaScript statements, Kitten also provides a number of useful custom commands you can use. These commands always begin with a dot.

For a full list of available commands, including ones inherited from Node.js’s own REPL, use the .help command.

.ls

The .ls command shows you the keys of Kitten’s global kitten object – one of the most useful objects you can access in Kitten’s shell.

It is a convenience function for the more verbose Object.keys(kitten) JavaScript statement that you can also use for the same purpose.

.id

Shows your Small Web place ID (ed25519 public key) if you have one.

Otherwise, prompts you to use the .id create command to create your Small Web place secret.

.id create

Creates your Small Web place secret (ed25519 private key, encoded as a lovely emoji string that you should keep in your password manager).

The output looks something like the following:

   🆔 Identity

 » New ID created.

🔑 This is your secret:
🍮🐅🚲💕🦔🍑🥚🐻🌳🐹🐈🐴🥝🐩🪀🐼🐊📚🦎🐕🎓👽🌭🍑🎂🥑🧬🌳🔭👻🩰🎮

.settings

Shows the URL for accessing the Kitten Settings page of your app.

e.g., If you’re running it in development on the default port, it’ll return:

https://localhost/🐱/settings/

.sign-in

Shows the Sign In page URL for authenticating with your Kitten app.

e.g., If you’re running it in development on the default port, it’ll return:

https://localhost/💕/sign-in/

.sign-out

Shows the Sign Out page URL for signing out of your current session on your Kitten app.

e.g., If you’re running it in development on the default port, it’ll return:

https://localhost/💕/sign-out/

.stats

Shows statistics about your Kitten app (including statistics on event handlers and listeners) as well as any other Kitten processes that might be active on your machine, as well as basic statistics on all running processes and on the machine itself.

Explore ways you can use Kitten’s shell (REPL) in the Kitten’s Interactive Shell (REPL) tutorial.

Command-line interface (CLI)

Use kitten help to access command information from your terminal.

To get detailed help on a specific command, use kitten help <command name>. For example, kitten help serve.

You can also pass the --help or -h option to any command to get more detailed information about it. So kitten serve --help is equivalent to kitten help serve.

Default command

serve

kitten [path to serve] [options]
kitten serve [path to serve] [options]

Serves a Kitten place.

💡 If do not specify a path to serve, the default directory (./) is assumed.

Options

When running multiple Kitten instances to test peer-to-peer web functionality during development, remember to use a different domain and port for each instance (for example, place1.localhost for one and place2.localhost:444 for the other) to ensure that sessions work correctly.

This is because Kitten sessions make sure of cookies and cookies do not provide isolation by port.

Kitten supports four localhost subdomains (place1 to place4) that can be run on any valid port for testing peer-to-peer Small Web apps locally.

An apps running on a random port gets a random data directory assigned to it. This directory is only valid for as long as the server is up. The next time you run the same app from a random port, it will get a different data directory and the data from the first run will not carry over. In other words, do not run your app on a random port if it uses a database and relies on data persistence between runs.

The random port feature is useful for local web apps that run locally on your device (the equivalent of command-line applications or native apps).

e.g., see the Markdown Preview utility in the examples folder.

Small Web apps are not meant to be run from random ports on production machines. Always deploy to run from port 443 in live environments (e.g., a VPS or a dedicated device like a single-board computer connected to your home router).

Database commands

db (alias for db info)

kitten db [table name]
kitten db info [table name]

Shows database and table information.

db delete

kitten db delete [table name]

Deletes either the whole database or the table with the specified table name after asking you for confirmation.

db tail

kitten db tail <table name>

Shows a summary of the head (start) and tail (end) of your database table (remember that database tables in JSDB are append-only JavaScript logs) and starts to follow additions to it.

Press Ctrl C to exit as you would a regular shell tail command.

Deployment of Kitten applications

deploy

kitten deploy <git HTTPS clone URL> [options]

Creates a Kitten place by deploying a Kitten project from a git repository as a systemd service.

Options

run

kitten run <git HTTPS clone URL> [options]

Is the little brother of the deploy command. Runs a Kitten project from a git repository locally as a regular process.

It’s options are the same as that of the default serve command.

Kitten daemon (systemd service) commands

status

kitten status

Shows Kitten systemd service status.

logs

kitten logs [options]

Tails (follows) the Kitten systemd service logs using journald.

Options

start

kitten start

Starts the Kitten systemd service.

stop

kitten stop

Stops the Kitten systemd service.

enable

kitten enable

Enables the Kitten systemd service (so it auto starts on boot and if the process exits)

disable

kitten disable

Disables the Kitten systemd service (so it no longer auto starts on boot, etc.)

General commands

version (aliases: --version and -v)

kitten version
kitten --version
kitten -v

Displays the Kitten version, made up of:

Eventually, you will be able to tell Kitten what API version your app is written for and it will only auto-update to the latest release in that API version.

help (aliases: --help and -h)

kitten help
kitten help <command name>
kitten <command name> --help
kitten <command name> -h

Use kitten help to access command information from your terminal.

To get detailed help on a specific command, use kitten help <command name>. For example, kitten help serve.

You can also pass the --help or -h option to any command to get more detailed information about it. So kitten serve --help is equivalent to kitten help serve.

update

kitten update
kitten update <API version>

Use kitten update to update (upgrade or downgrade) your currently installed version of Kitten.

When you run the command without supplying the optional API version argument, Kitten will attempt to update to the more recent publicly-available Kitten package from kittens.small-web.org. If your version of Kitten is more recent than the most recent publicly-available Kitten package, Kitten will prompt you to ask if you want to downgrade your installation.

If you supply the optional API version argument, the update will check for the latest version for the given API version. If such an API version does not exist, you will get an error. If your version of Kitten is already on that API version but is a more recent build, you will be prompted whether you want to downgrade your installation.

The only time the downgrade prompt should show is if you’re running a local development build that is more recent that the most recent publicly-available Kitten package. This is useful if you want to quickly test a publicly-available package during development. You can, of course, always install the latest development version by running the ./install script as usual.

uninstall

kitten uninstall

Uninstalls Kittens and removes all Kitten data after asking for confirmation.

Troubleshooting

Here are some edge cases you might encounter (because others have encountered them) and what you can do about it:

Linux: Untrusted localhost TLS certificates in Chrom(ium)

If at some point Chrom(ium) starts complaining that your development-time localhost certificates are untrusted under Linux, check that you haven’t accidentally installed a copy of ca-certificates and/or p11-kit in your unprivileged account using a third-party package manager like Homebrew (brew).

If this is the issue, you should:

  1. Remove the ca-certificates and p11-kit packages from Homebrew (you will also have to remove any packages that depend on them. e.g., python, OpenSSL, etc.)
  2. Restart your machine.
  3. Run sudo update-ca-trust extract
  4. Uninstall Kitten (kitten uninstall)
  5. Reinstall Kitten via the one-line installation command or from your working copy of the source code (./install)

That should do it.

To check if your system trust store is set up correctly, you can run the following command:

trust list --filter=ca-anchors | grep Localhost

And you should see output that resembles the following:

label: Localhost Certificate Authority for aral at dev.ar.al

Building Kitten

While developing Kitten, it’s best practice to run the install script and use the kitten command to run your installed build.

./install

A typical run of the install commands takes about half a second on a modern computer so it should not impact your development velocity negatively.

In order to keep the development build/install process as quick as possible, dependencies are not updated unless you specifically request an npm install by passing the --npm flag:

./install --npm

(However, an npm install will be carried out if this is the first time you’re building/installing Kitten locally.)

There is a separate build command (called internally by the install script) and if you use that, you will find the distribution under the dist/ folder.

To run Kitten from the distribution folder, use the following syntax:

dist/kitten [path to serve]

Kitten’s build + install process (what happens when you run the install script) takes only about two seconds on a modern computer and has the additional benefit of informing you of compile-time errors.

It’s highly recommended you don’t run build by itself or run from the dist folder directly unless you have specific reason to. During everyday development, just run the install script.

Debugging

To run Kitten with the Node debugger active (equivalent of launching Node.js using node --inspect), start Kitten using:

INSPECT=true kitten

If you use VSCodium, you can add breakpoints in your code and attach to the process using the Attach command in the Run and Debug panel.

In Chromium, you can use the Node debugger (enter chrome://inspect in address bar → select ‘Open dedicated DevTools for Node’).

Profiling

You can see profiling information provided by Kitten’s console mixin methods profileTime() and profileTimeEnd() by passing the PROFILE=true environment variable:

e.g.,

PROFILE=true kitten examples/streamiverse

Flame Graphs

To narrow down performance issues by time spent on the stack, you can have Kitten generate a flame graph by setting the FLAME_GRAPH environment variable to true.

e.g.,

FLAME_GRAPH=true kitten examples/streamiverse

This will start your project in production mode and globally install and use the 0x node module to capture profiling information and automatically launch a browser when the process exits to show you the flame graph.

Flame graphs can only be generated in production mode as the 0x module is not compatible with Node’s cluster module and the latter is what we use to implement Kitten’s process manager in development mode.

Testing

Kitten’s tests are a work-in-progress and steadily improving.

There are three types of tests in Kitten:

Unit tests

Unit tests are written in Tape With Promises, run using ESM Tape Runner, and displayed using Tap Monkey.

Coverage is provided by c8.

Run tests:

npm -s run unit-tests

Run coverage:

npm run -s coverage

The -s flag just silences the npm logs for cleaner output.

Unlike regression tests and end-to-end tests (see below), unit tests are run on the Kitten source code instead of the built Kitten bundle.

Regression tests

Regression tests are written with the same stack as Kitten’s unit tests but use the Kitten and Browser test helpers to test initial output from test fixtures (the example apps).

These do not test client-side interactions, for that, please see end-to-end tests, below.

(In the future, these tests might be removed in favour of only keeping the end-to-end tests as they test similar things. The only advantage of the regression tests is that they are faster to run as they test using JSDOM instead of actual browsers.)

Run tests:

npm run -s regression-tests

Regression tests initially perform a Kitten install to ensure you are testing with the latest Kitten bundle. If you do not want this, run the regression-tests-without-install tast instead of the regression-tests task.

End-to-end tests

End-to-end tests use Playwright, along with our custom Kitten helper for controlling the Kitten server to test Kitten’s behaviour using actual browser engines (Safari/Webkit, Firefox/Gecko, Chromium/Blink).

Run tests:

npm run -s end-to-end-tests

Like regression tests, end-to-end tests perform Kitten install prior to running the tests. To skip this, run the end-to-end-tests-without-install task.

By default, tests run in headless browsers. You can also run the tests in Chromium with the interface showing so you can see the state of the web interface at every state of the tests. To run the tests this way, execute the end-to-end-tests-in-browser task. There is also a end-to-end-test-in-browser-without-install task you can use to skip the install Kitten install.

All tests

To perform a Kitten install and run all tests:

npm -s test

This will run unit, regression, and end-to-end tests.

Deployment (of Kitten itself)

To build and deploy a new Kitten package to kittens.small-web.org,run the deploy script:

./deploy

You will need to add the secret deployment token to your system in order to deploy. If you have deployment rights for Kitten, follow the instructions available at https://kittens.small-web.org/settings.

Core dependencies

DependencyPurpose
@small-tech/httpsDrop-in replacement for Node’s native https module with automatic TLS for development and production using @small-tech/auto-encrypt and @small-tech/auto-encrypt-localhost.
@small-tech/jsdbZero-dependency, transparent, in-memory, streaming write-on-update JavaScript database that persists to JavaScript transaction logs.
Polka@nextNative HTTP server with added support for routing, middleware, and sub-applications. Polka uses Trouter as its router.
tinywsWebSocket middleware for Node.js based on ws.
xhtmXHTM is alternative implementation of HTM without HTM-specific limitations. Low-level machinery is rejected in favor of readability and better HTML support.
hyperscript-to-html-stringRender xhtm/Hyperscript to HTML strings, without virtual DOM.
sanitize-htmlClean up untrusted HTML, preserving whitelisted elements and whitelisted attributes on a per-element basis. Built on htmlparser2 for speed and tolerance
node-git-serverGit server for hosting your source code. Used in deployments.
isomorphic-gitGit client used in deployments on development and for handling auto-updates on production.
sadeA small command-line interface (CLI) framework that uses mri for its argument parsing.
noble-ed25519Cryptography: ed25519 key generation.
ed25519-keygenCryptography: SSH key generation from ed25519 key material.

Kitten’s renderer is built upon xhtm and vhtml although both of those libraries have been inlined and extended.

For an automatically-generated full list of modules and contributors, please see CONTRIBUTORS.md.

Cryptographic properties

(The functionality described in this section is currently being developed both in Domain and Kitten.)

Overview

The security (and privacy) of Domain/Kitten are based on a 32-byte cryptographically random secret string that only the person who owns/controls a domain knows.

This is basically a Base256-encoded ed25519 secret key where the Base256 alphabet is a set of curated emoji surrogate pairs without any special modifiers chosen mainly from the animals, plants, and food groups with some exceptions (to avoid common phobias or triggers, etc.) that we call KittenMoji.

🐵🐒🦍🦧🐶🐕🦮🐩🐺🦊🦝🐱🐈🦁🐯🐅
🐆🐴🧮🦄🦓🦌🦬🐮🐂🐃🐄🐷🐖🐗🐽🐏
🐑🐐🐪🐫🦙🦒🐘🦣🦏🦛🐭🐁🐀🐹🐰🐇
🎈🦫🦔🦇🐻🐨🐼🦥🦦🦨🦘🦡🐾🦃🎹🐓
🐣🐤🐥🐦🐧💕🦅🦆🦢🦉🦤🪶🦩🦚🦜🚲
🐊🐢🦎📚🐉🦕🦖🐳🐋🐬🦭🐟🐠🐡🦈🐙
🐚🐌🦋🐛🐜🐝🪲🐞🦗🎭🎁🧬🪱🦠💐🌸
🎠🌈🌹🧣🌺🌻🌼🌷🌱🪴🌲🌳🌴🌵🌾🌿
🎤🍀🍁🪺👽🍇🍈🍉🍊🍋🍌🍍🥭🍎🍏🍐
🍑🍒🍓🫐🥝🍅🫒🥥🥑🍆🥔🥕🌽🧸🫑🥒
🥬🥦🧄🧅🍄🥜🌰🍞🥐🥖💩🥨🥯🥞🧇🧀
🎶🏸🎾🎨🍔🔭🍕🌭🥪🌮🌯😸📷🌜🥚🚂
🛼🚁👾👻🥗🍿🧩🖖🥫🎸🍘🍙🍚🃏🍜🍝
🍠🍢🍣🍤🍥🥮🍡🥟🥠🩰🦀🦞🦐🦑🎡🍦
🍧🍨🍩🍪🎂🍰🧁🥧🍫🍬🍭🍮🎓🍼🎮🛹
🫖🌍🌎🌏🧭🌠🪐🪀🧵🧶🧋🎉🪁🙈🙉🙊

The KittenMoji standard was frozen in time on Friday, November 25th, 2022.

The string created by joining the list of the full KittenMoji alphabet has the following hash:d9b1533de36a75c9e47f1713aada4ff332918ff0d3cada5b645ca84d73dd50d6.

When setting up a Small Web app via Domain, this key is generated in the person’s browser, on their own computer, and is never communicated to either the Domain instance or the Kitten app being installed. Instead the ed25519 public key is sent to both and signed token authentication is used when the server needs to verify the owner’s identity (e.g., before allowing access to the administration area).

The expected/encouraged behaviour is for the person to store this secret in their password manager of choice.

From this key material, we derive SSH keys and the person’s server is set up to allow SSH access via this key.

Kitten running on a development machine can also recreate these SSH keys and configure the person’s SSH access to their server when the secret key is provided.

The person’s ed25519 public key is used as their identity within the system and enables their Small Web place (Kitten app) to communicate with the Domain instance for administrative reasons (e.g., to cancel hosting, etc.)

Threat model

The audience for Domain/Kitten and the Small Web in general is everyday people who use technology as an everyday thing and want privacy by default in the systems they use.

If you are an activist, etc., who might be specifically targetted by nation states, etc., please do not use this system as it does not protect against, for example, infiltration of hosting providers by nation state actors.

Given that the secret key material is generated in a web app, you must trust that the code being served by the server is what you expect it to be. Currently, there is no validation mechanism for this, although this is on the roadmap going forward (via, for example, a browser extension). This is not a problem unique to web apps, given that native apps are commonly dynamically built by app stores (at which point a nation state actor could inject malicious code) and given the lack of verifiable builds in mainstream supply chains.

You can, however, overcome this limitation by hosting Domain yourself, on your own hardware (e.g., a single-board computer like a Raspberry Pi).

The other thing to be aware of is that the security of the system is based on your secret key remaining secret and, initially at least, there will not be a way to change this secret key. (On the longer roadmap, it would be nice to provide a means of changing it but this is not a trivial process, especially if encryption keys have been derived from it and encrypted messages exist within a Kitten app).

Finally, as of September 2024 when this was last updated, the major browser engines do not yet all support ed25519/x25519 in the Web Crypto API. Specifically, while Apple (Safari) and Mozilla (Firefox) have implemented it, Google (Chrome/Chromium) hasn’t. This means that we must use a JavaScript library to handle the cryptography ad store the secret key in local storage on the client. Therefore, if a Kitten app is compromised via a Cross-Site Scripting (XSS) attack, the secret can be obtained. Once Chrome and Chromium-based browsers implement the feature by default (it is currently behind a flag), Kitten will migrate to using the Web Crypto API and the secret key will be stored in an inextractable manner that is safe from exposure via XSS.

Install Kitten on Windows under WSL 2

Windows is an ad-infested and surveillance-ridden dumpster fire of an operating system and you are putting both yourself and others at risk by using it.

Kitten is not supported on Windows although you might be able to get it running under Windows 10 & 11 under WSL 2 using the instructions here.

Please do not open issues for Windows-specific issues as they will not be fixed.

Note following caveats:

  1. Requires WSL 2 (will not work with WSL 1).
  2. You must manually install Kitten’s local development-time certificate authority in your Windows browsers.

If you haven’t installed Kitten on Windows or used WSL 2 before, the instructions, below, take you through the whole process.

The process of installing Kitten on Windows is not as seamless as it is on Linux and macOS and it is not guaranteed to work or continue working. Again, Kitten is not supported on Windows and as part of that, it is not tested on Windows.

Instructions

You must first install WSL 2 and you must manually add Kitten’s local development-time certificate authority to the trust stores of your Windows browsers to avoid certificate errors:

  1. Open up a Windows Powershell tab in Terminal App.

    Windows comes with a number of terminal apps and shells. Make sure you use the exact pair mentioned above.

  2. Install WSL 2:

    wsl --install
    

    This will install WSL 2 and Ubuntu. It will ask you to choose an account name and set a password and then you will be running Ubuntu under WSL2.

    Kitten only works with WSL 2 using Windows Terminal and Windows Powershell (the sysctl command used by the installer fails on systems with WSL 1).

    To make sure your Linux container is running under WSL 2 (and not 1) before continuing, run:

    uname -a
    

    On my system, that gives me the following system information:

    Linux aral-win11 5.15.90.1-microsoft-standard-WSL2 #1 SMP Fri Jan 27 02:56:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
    

    Note that the string WSL2 is contained in the output. If you don’t see that, you’ll have to either create a new container using WSL 2 or update your current container to use WSL 2.

    For best results, please make sure you’re running the latest Ubuntu version that’s installed automatically by wsl when you run the wsl --install command.

    (If you’re running a very old version of Ubuntu, it may not have the new Let’s Encrypt certificate authority in its trust store and the Kitten installer may fail with a certificate error. If this happens, please either upgrade your version of Ubuntu or try tunning sudo dpkg-reconfigure ca-certificates to download the latest list of trusted certificate authorities into the system store.)

  3. Install Kitten:

    wget -qO- https://codeberg.org/kitten/app/raw/install | bash
    

    (Enter the password you set up for your Ubuntu installation under WSL 2 when asked.)

  4. Source your .profile to add Kitten’s binary to your path:

    source .profile
    

    (Kitten is now installed properly but Ubuntu doesn’t add the ~/.local/bin folder that Kitten puts its binary into to your system path until that path exists on your computer. Instead of sourcing the .profile, you can also log out and log back in.)

  5. Test the Kitten binary.

    kitten --version
    

    You should see Kitten launch and the version get displayed.

  6. Create your first project and run Kitten.

    Don’t skip this step! The first time Kitten runs, it creates your local development-time certificate authority (CA) and TLS certificate. You will need the CA to install in your Windows browsers in the next section.

    mkdir hello-kitten
    cd hello-kitten
    echo 'Hello, Kitten!' > index.html
    kitten
    

    Kitten should launch and start serving your site.

    However, if you open a web browser under Windows (e.g., the default Edge browser), you will see a NET::ERR_CERT_AUTHORITY_INVALID security error warning you that your connection isn’t private.

    This is because Kitten is running under Ubuntu and your browser is running under Windows. While Kitten can (and does) update your Linux system trust store to accept its local development certificates, it cannot do that for your Windows machine because it doesn’t even know that it exists.

    So, on Windows, you have to manually install Kitten’s certificate authority in your Windows browsers.

    The next section takes you through doing that.

Manually install the certificate authority in your Windows browsers.

For browsers you have installed under Windows to accept the TLS certificates created by Kitten, you must install its certificate authority into your browsers. (Remember that Kitten is running under Linux and doesn’t know anything about your Windows environment so it can’t do it for you.)

For example, if you’re using Microsoft Edge:

  1. Go to edge://settings/privacy, scroll down to the Security section, and select “Manage certificates.”

    Screenshot of Settings page in Microsoft Edge.
  2. In the resulting “Certificates” modal, select the Trusted Root Certificate Authorities tab and press the Import… button.

    Screenshot of the Certificates modal showing the empty Personal tab selected
  3. In the resulting Certificate Import Wizard, press Next to pass from the welcome screen to the File to Import step and press the Browse… button.

    Screenshot of the File to Import stage of the Certificate Import Wizard with an empty File name textbox and the Browse… button mentioned in the text.
  4. Make sure the file extensions drop-down is set to show all extensions and select Linux → Ubuntu → home → (your home directory) → .local → share → small-tech.org → kitten → tls → local → auto-encrypt-localhost-CA.pem.

    Screenshot of the file dialogue showing the auto-encrypt-localhost-CA.pem certificate selected.
  5. In the next step, make sure “Place all certificates in the following store” is selected with “Trusted Root Certification Authorities” set.

    Screenshot showing the Certificate Import Wizard with the settings mentioned in the step applied.
  6. Press Next and, on the final screen, press Finish.

    Screenshot of Certification Import Wizard: Completing the Certificate Import Wizard. The certificate will be imported after you click Finish. (Contains a summary of the selected certificate settings.)
  7. After the wizard closes, you will see a Security Warning telling you that you are about to install a root certificate. Select Yes to continue.

    Screenshot of Security Warning modal: “You are about to install a certificate from a certification authority (CA) claiming to represent: Localhost Certificate Authority for aral at aral-win11. Windows cannot validate that the certificate is actually from …”

Finally, restart Edge and you should be able to hit https://localhost without certificate errors.

Now that you’ve successfully installed Kitten on Windows under WSL 2, you can continue learning about Kitten by following the Getting Started guide and tutorials.

Questions?

Contact Aral on the fediverse.

Like this? Fund us!

Small Technology Foundation is a tiny, independent not-for-profit.

We exist in part thanks to patronage by people like you. If you share our vision and want to support our work, please become a patron or donate to us today and help us continue to exist.