Documentation

Running a server·Updated 13 Oct 2026

The CLI and desktop downloads

How the server hands out the kribble CLI, the editor extensions and the desktop app, how they update, how to build the desktop app, and how to run your own downloads site

Each Kribble server hands out the tools that go with it: the kribble command-line tool, the VS Code and JetBrains extensions and, if you add them, the desktop apps. They come from the same release as the server, so their versions match. How people use them is in The kribble CLI and The desktop app.

What the server serves

kribble-web serves the contents of [downloads].dir (by default downloads in its working directory: /opt/kribble/downloads with install.sh, /app/downloads in Docker):

Path

What

/install.sh

Shell installer for the CLI on macOS and Linux

/install.ps1

PowerShell installer for the CLI on Windows

/downloads/manifest.json

The release version and the list of files, with SHA-256 checksums

/downloads/<file>

A file listed in the manifest. Anything not listed returns 404.

/downloads/<file>.sha256

The file's checksum

/downloads/jetbrains/updatePlugins.xml

A JetBrains plugin repository for the Kribble Markdown plugin

Signed-in users find install commands and the desktop installers on the CLI & agents page of the web app.

The directory comes from the release:

Kind

Files

Built by

CLI

kribble-<os>-<arch> for Linux, macOS and Windows on amd64 and arm64

pnpm build:release (dev/build.ts), or deploy/build-cli-downloads.sh in the Docker image

VS Code extension

kribble-markdown.vsix

pnpm build:release. KRIBBLE_SKIP_EXTENSIONS=1 skips both extensions.

JetBrains plugin

kribble-intellij.zip

pnpm build:release, only when a JDK and the JetBrains repositories are available. Otherwise the release is built without it and prints a warning.

Desktop app

installers and updater files

Built per OS and added with node dist/add-desktop.mjs (below)

install.sh replaces /opt/kribble/downloads with the tarball's copy on every install and update. The Docker image built from deploy/Dockerfile contains only the CLI. Without the directory the server works, but offers nothing to download.

Installing the CLI

On any computer:

curl -fsSL https://kribble.example.com/install.sh | sh     # macOS, Linux
irm https://kribble.example.com/install.ps1 | iex          # Windows PowerShell

The script downloads the build for the computer, checks its SHA-256, installs it (~/.local/bin, or /usr/local/bin as root, or %LOCALAPPDATA%\Programs\kribble on Windows; KRIBBLE_INSTALL_DIR overrides it) and saves the server's address, so kribble login needs no --server.

The scripts contain the server's address as the client used it: the Host (or X-Forwarded-Host) and X-Forwarded-Proto headers your proxy passes. If they print http:// or an internal host name, fix the proxy (Installing).

JetBrains IDEs can install and update the plugin from the server: add https://kribble.example.com/downloads/jetbrains/updatePlugins.xml under Settings → Plugins → ⚙ → Manage Plugin Repositories. kribble extension install installs the extensions from the server into VS Code-based editors and JetBrains IDEs.

Updates

  • The CLI. kribble update downloads the version the active profile's server offers, so the CLI matches the server. Without any server set up, it updates from https://getkribble.com. When the CLI and the server versions differ, the CLI prints a note asking the user to run kribble update.

  • The desktop app checks for updates at <server>/downloads/ of the active profile (electron-updater's generic provider, reading latest*.yml). Before any server is added, it uses https://getkribble.com. Updates download in the background and install when the app quits.

So when you deploy a new release, the CLIs and desktop apps connected to your server move to it. If the release has no desktop files, installed apps stay as they are.

Building the desktop app

The desktop app is built separately on each OS, then added to the release tarball. Build every OS from the same commit as the server release. The app's version comes from git tags: v1.4.0 builds 1.4.0, and later commits build 1.4.1-N.

Build the server release

At the repository root:

pnpm build:release

This writes dist/kribble-linux-amd64.tar.gz and, next to it, dist/add-desktop.mjs.

Build the app on each OS

Each needs the repository, Node, pnpm and Go:

Command

Output in dist-desktop/<os>/

pnpm build:desktop:mac

dmg and zip for Apple silicon, signed and notarized when configured

pnpm build:desktop:win

the x64 installer .exe, unsigned. Off Windows it needs Wine, or it makes a zip that doesn't update itself.

pnpm build:desktop:linux

AppImage and deb, x64 and arm64

pnpm build:desktop:linux:docker

the Linux build in a container, from a Mac with Docker

Ship all the files, including the updater's (latest*.yml, .blockmap, the mac .zip).

Add them to the tarball

Copy the files next to dist/add-desktop.mjs and run:

node dist/add-desktop.mjs

It adds them to the tarball's downloads/ and its manifest. Adding a platform again replaces that platform's files. Then deploy the tarball as usual.

The Release workflow in .github/workflows/release.yml does all of this on GitHub when started by hand (Actions → Release → Run workflow), and attaches the tarball to a draft release. It signs and notarizes the Mac app when these repository secrets are set: MAC_CERT_P12_BASE64, MAC_CERT_PASSWORD, APPLE_API_KEY_P8, APPLE_API_KEY_ID, APPLE_API_ISSUER.

Signing and notarizing on a Mac

pnpm build:desktop:mac reads .env at the repository root (it is git-ignored). Without these settings it still builds, unsigned or not notarized, and says so.

Variable

What it is

CSC_NAME

Which Developer ID Application certificate in the login keychain to use, if there are several, for example Developer ID Application: Your Name (TEAMID1234)

CSC_LINK, CSC_KEY_PASSWORD

On another machine or CI: the certificate exported as .p12, and its password

APPLE_API_KEY, APPLE_API_KEY_ID, APPLE_API_ISSUER

Notarization with an App Store Connect API key: the path of the AuthKey_….p8 file, its key ID and the issuer ID (recommended)

APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID

Notarization with an Apple ID and an app-specific password instead

Signing also needs the Xcode command line tools (xcode-select --install). desktop/DESKTOP.md in the repository has the step-by-step for getting the certificate and the API key.

Running your own downloads site

kribble-www is a small separate server for a public downloads site like getkribble.com: a landing page with the CLI, its install commands and the desktop apps, plus /install.sh, /install.ps1, /downloads/*, llms.txt, sitemap.xml and /healthz. It has no database or config file and serves the downloads/ of the release it was installed from. It doesn't need a Kribble server.

Its install scripts don't set a server, so people installing from it sign in with kribble login --server <url>. The CLI and the desktop app use getkribble.com, which is built into them, for updates until a server is set. Your own downloads site is not used for those updates.

On a Linux host

The release tarball includes it. Add the desktop apps to the tarball first if the site should offer them. Then:

tar xzf kribble-linux-amd64.tar.gz
sudo bash install-www.sh

This installs /opt/kribble-www/kribble-www and /opt/kribble-www/downloads, creates the user kribble-www and the unit kribble-www.service, and starts it on port 8080. KRIBBLE_WWW_PORT and INSTALL_DIR change the port and location. Run it again with a newer tarball to update.

kribble-www takes --addr (or KRIBBLE_WWW_ADDR, default :8080) and --downloads (or KRIBBLE_WWW_DOWNLOADS, default downloads). Put a TLS proxy in front that passes the host and scheme, so the install commands on the page carry the right address:

downloads.example.com {
    reverse_proxy localhost:8080
}

As static files on Cloudflare Pages and R2

kribble-www --export writes the site as static files instead: the page and install scripts for Cloudflare Pages, and the downloads, which are too big for Pages' 25 MB file limit, for an R2 bucket. /downloads/* on the site redirects to the bucket, and the install scripts, kribble update and the desktop app follow the redirect.

From a release tarball (with the desktop apps added, if wanted):

pnpm build:www                                   # uses dist/kribble-linux-amd64.tar.gz
pnpm build:www ~/Downloads/kribble-linux-amd64.tar.gz --site https://downloads.example.com --files https://dl.example.com

--site and --files default to https://getkribble.com and https://dl.getkribble.com. The result is dist/www/files (for R2) and dist/www/site (for Pages, with _redirects and _headers). Without --files, kribble-www --export puts the downloads into the site itself, for static hosts that accept large files.

Set up Cloudflare once:

  1. Move the domain's DNS to Cloudflare.

  2. Create an R2 bucket and connect the files domain (dl.example.com) to it as a custom domain. The r2.dev address doesn't go through the cache.

  3. Add a Cache Rule for the files domain that makes requests eligible for cache. Cloudflare doesn't cache files without an extension, like kribble-linux-amd64, otherwise. Downloads served from the cache don't count as R2 reads.

  4. Create a Pages project for the site's domain.

  5. Optionally add a rate-limiting rule on the files domain and a billing notification. R2 has no spending cap. Past the free tier it charges for reads and storage, not traffic.

For each release, upload the files before the site, so the new page never links to files that aren't there yet:

rclone sync dist/www/files r2:<bucket> --header-upload "Cache-Control: public, max-age=300"
npx wrangler pages deploy dist/www/site --project-name <pages-project>

Then purge the cache, because the CLI files keep the same names from release to release. rclone sync also deletes the previous release's files. Files uploaded from the Cloudflare dashboard have no Cache-Control, so set an Edge TTL (for example an hour) in the cache rule if you upload that way.