Installing¶
As a container¶
Every release tag publishes ghcr.io/junkerderprovinz/knightloader for amd64
and arm64. The image carries yt-dlp, ffmpeg and the Java runtime the headless
JDownloader needs, so nothing else has to be installed.
docker run -d --name knightloader \
--restart unless-stopped \
-p 8749:8749 \
-v /path/to/appdata:/data \
-v /path/to/downloads:/data/downloads \
-e TZ=Europe/Berlin \
ghcr.io/junkerderprovinz/knightloader:latest
Then open http://<host>:8749.
The container starts as root only for a moment. Folders that Docker has just
created for it, which belong to root and are empty, are given to uid and gid
1000; then KnightLoader switches to that account and runs as it from then on. A
folder that already holds something keeps its owner, and if KnightLoader cannot
write it, the log names the chown to run on the host. -e PUID=<uid> and
-e PGID=<gid> choose another account.
On Unraid, install KnightLoader from Community Applications. Its template puts
--user 99:100 in Extra Parameters and sets the variable UMASK to 000.
Finished files then land as nobody:users with mode 0666 and their folders
with 0777, so the account you use over SMB can
move, rename and delete them. UMASK is read the way linuxserver.io images
read it, as an octal mask such as 000, 002 or 022. With --user the
container starts as that account straight away, and PUID and PGID are not
read. Settings, Health & diagnostics shows which user and mask are in force.
App key¶
KL_APP_KEY makes the secrets in the data folder useless without it. The login
password is hashed with Argon2id and peppered with the key. The session key,
the second factor and its recovery codes, the key that seals the stored
accounts, every password, token and command line in settings.json, and the
archive and download passwords stored with each download are sealed with it,
and so are the backups: the one you download from Settings and the ones
written on a schedule. Somebody who
copies the appdata folder, through a backup plugin for instance, gets nothing
they can use.
Generate a value once and keep it with your other passwords:
Pass it as -e KL_APP_KEY=<the 64 characters>, or on Unraid add a variable
named KL_APP_KEY to the template, and restart. The value has to stay the same
from then on. KnightLoader refuses to start on a data folder sealed with a
different key, or with none, instead of overwriting it.
An install that ran without the key seals its files at the first start with
it. It copies them to a before-app-key-<date> folder in the data folder first,
so a sealing that goes wrong can be undone by hand. The copies are as readable
as the files were, so KnightLoader deletes the folder itself once it has run
for seven days without a value failing to seal or open; each such failure, a
start with the wrong key included, starts the seven days over. Until then the
Security page says when the folder goes. The password itself moves to Argon2id
the next time you sign in. Without the variable everything works as before. On
the Security page the info bubble beside App key says what the key does and
how to add one.
A sealed backup ends in .zip.sealed. On an instance with another key or none,
enter the key of the instance that made it with the upload; everything in it
is then sealed again with the key of the instance it lands on. Backups made
before the key still restore. A backup without a key holds every password
readable.
Behind a reverse proxy¶
KnightLoader works on a host name of its own, such as https://kl.example.com,
and just as well in a folder of another one, such as https://example.com/kl/.
Either way the proxy has to pass the WebSocket upgrade through and, when it
terminates TLS, send X-Forwarded-Proto. The reverse proxy check under
Settings, Network checks both from your browser.
For a folder, KnightLoader has to know the path. There are two ways to tell it:
- Set
KL_BASE_PATH=/klin the container's environment. The proxy may pass the prefix on or strip it. - Set nothing, and let the proxy strip the prefix and name it in
X-Forwarded-Prefix. Traefik's StripPrefix middleware sends that header by itself; in nginx addproxy_set_header X-Forwarded-Prefix /kl;.
If both are there, KL_BASE_PATH wins. The prefix has to be a plain path:
letters, digits and -._~ between the slashes. It cannot begin with /api,
/relay or /dav, since the instance answers those without the prefix too.
nginx, passing the prefix on, with KL_BASE_PATH=/kl:
location /kl/ {
proxy_pass http://knightloader:8749;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Traefik, stripping it, with nothing set:
labels:
- traefik.http.routers.knightloader.rule=Host(`example.com`) && PathPrefix(`/kl`)
- traefik.http.routers.knightloader.middlewares=knightloader-strip
- traefik.http.middlewares.knightloader-strip.stripprefix.prefixes=/kl
- traefik.http.services.knightloader.loadbalancer.server.port=8749
Caddy, passing it on, with KL_BASE_PATH=/kl. The redir line sends the bare
/kl to /kl/, which handle /kl/* would not match. handle_path in place of
handle works too: it strips the prefix, and KL_BASE_PATH still names it.
Once the path is known, the instance uses it everywhere: the session cookie,
the installed web app, its share target and the bookmarklet all include it.
Where you name the instance yourself, include the path too: for the
Click'n'Load bridge (-bridge https://example.com/kl), for another instance
added by address on the Instances page, and as the relay address on the
others when this one has "Instance as relay" chosen in the relay card under
Settings, Pairing. The phone app and the browser extension reach the instance
through the relay with the twelve words and need no address.
The path does not separate the instance from the other applications on the host. They share one origin with it, so a page served by any of them can call KnightLoader's API with your session, whatever path the cookie carries. Only a host name of its own keeps them apart, so give it one unless you trust everything else on that host.
The container's health check and the LAN address http://<host>:8749 keep
working without the prefix.
Building the image yourself¶
docker build --build-arg VERSION=preview --build-arg COMMIT="$(git rev-parse HEAD)" -t knightloader:preview .
Pass both arguments. .dockerignore keeps .git out of the build context, so
the toolchain inside the image has no repository to read, and without COMMIT
the binary cannot tell which revision it is. VERSION shows under the
wordmark, and COMMIT answers as commit on GET /api/health.
As a desktop app¶
The desktop app is the same binary in a native window instead of a browser tab. The engine, the resolvers, the API and the interface are identical, because the window is served by the same HTTP handler the container serves.
Every release tag builds windows/amd64, windows/arm64, darwin/universal,
linux/amd64 and linux/arm64, and attaches each archive to
that release.
Take x64 for most computers and ARM64 for one with an ARM processor, such as a
laptop with a Snapdragon chip. The macOS bundle is universal, so it runs on
Intel and on Apple silicon. A running instance offers the same downloads under
Settings, Apps, and picks the matching build when the browser reports the
processor.
Windows and Linux also get an installer beside each zip. On Windows it puts
KnightLoader under C:\Program Files\KnightLoader for everyone on the computer,
or into another folder you pick, and asks for an administrator once, while it
installs; later updates ask nobody (see Updates). Two switches
decide on a Start menu entry and a desktop shortcut, both on at first, and both
go to every user. The next install, silent or not, starts from your answer and
removes a shortcut you left out. On the way it removes an installation for you
alone under AppData\Local\Programs, and it never touches your settings,
accounts and private JDownloader in %APPDATA%\KnightLoader. The
KnightLoader.exe in the zip is the same program as a portable copy that needs
no installing.
On Linux the installer is for you alone and asks for no password. It puts
KnightLoader in ~/.local/share/com.junkerderprovinz.knightloader and adds it
to the application menu, with a desktop icon if you want one. Right-clicking
the menu entry offers to uninstall it. That copy updates itself like the one
from the zip. Make the downloaded file executable first
(chmod +x knightloader-linux-amd64-installer).
The desktop app brings yt-dlp for video sites: the installer puts it beside the program and the macOS app carries it inside. The Windows and Linux zips hold the program alone, so a copy started from one fetches yt-dlp into its data folder when it starts. Either way the Updates card under Settings, General keeps it current.
ffmpeg and ffprobe, which join video with its audio and convert, are not in
the download. On Windows and Linux the desktop app fetches the LGPL build from
FFmpeg-Builds into its data
folder at the first start when there is no ffmpeg on PATH, checked against
that release's checksums, and renews it once a month. On macOS, where that
project builds nothing, put ffmpeg on PATH yourself. An ffmpeg already on
PATH is always the one used.
The private JDownloader runs on a Java runtime of KnightLoader's own, whatever
Java the computer has or lacks. At the first start the desktop app fetches
Eclipse Temurin 21 from Adoptium's releases
on GitHub, checks it against the SHA-256 that KnightLoader carries for this
version and unpacks it into the jre folder of its data folder
(%APPDATA%\KnightLoader\jre, ~/Library/Application Support/KnightLoader/jre,
~/.config/KnightLoader/jre). That takes a few minutes, which the
JDownloader row on the Updates card shows; a failed download says why there
and offers to try again. A KnightLoader update that pins a newer runtime
replaces it at its next start. In between, the app asks Adoptium once a week
for a newer Temurin 21 while either of JDownloader's update switches is on.
With Update automatically it fetches the new runtime, checks it against
Adoptium's SHA-256 and moves JDownloader onto it at a moment when none of its
downloads runs. The old runtime stays until JDownloader has started on the new
one, and comes back if it does not. Windows on Arm gets the x64 runtime, which
Windows runs under its emulation, since Adoptium builds no Arm JRE for it.
Temurin comes under the GPLv2 with the Classpath Exception. Pointing KL_JD
at a JDownloader that runs elsewhere skips all of this.
On Linux the window needs WebKitGTK: libwebkit2gtk-4.1-0.
The desktop app makes its own app key at the first start and keeps
it in the operating system's credential store (Windows Credential Manager, the
macOS Keychain, the Secret Service on Linux) as app-key under KnightLoader.
Its data folder and its backups then open only with that key. To move them to
another computer, copy the value and, before KnightLoader first starts there,
store it under the same name or set it as KL_APP_KEY. Without a usable
credential store the app runs without a key, as before.
While a download is running, or its files are being checked, unpacked or moved, the desktop app keeps the computer from going to sleep, and once nothing is left to do it lets it sleep again. The screen can still turn off. The switch is on the Automation page, at the top of the Idle card. On Linux the app asks logind for a sleep lock, which a local desktop session gets without a password.
The Flatpak¶
Every release also attaches knightloader-linux-amd64.flatpak and
knightloader-linux-arm64.flatpak. Open one in your software center, or
install it from a terminal:
Flatpak fetches the GNOME runtime it runs on from Flathub. The app keeps its
data in ~/.var/app/io.github.junkerderprovinz.knightloader/config/KnightLoader
and fetches yt-dlp, ffmpeg and its Java runtime there, as a copy from the zip
does. It can save downloads anywhere in your home folder and on the drives
under /media, /run/media and /mnt. A download folder anywhere else needs
an override first:
The Flatpak never replaces itself, so Update automatically stays off there:
install the newer bundle to update it. It is built from the same sources as the
other downloads but without the DLC service key, so a .dlc file goes to
JDownloader. It keeps the computer awake through the desktop's Inhibit portal,
but cannot put it to sleep once the queue is done, so the Idle card does not
offer that there. On Wayland the clipboard watch sees a copied link once the
window is in front, since the runtime has no wl-paste.
Updates¶
The desktop app updates itself from GitHub. It asks for the newest release,
downloads the zip for your system in the background and checks it against the
release's checksums.txt. The new version starts the next time you open
KnightLoader, so nothing changes while it runs, and a note in the corner of the
window says when it is ready. Pre-releases are never installed. Update
automatically on the General page of Settings turns this off; it is on
from the start. When Check for updates on the Updates card finds a newer
release, Install now fetches it at once instead of waiting for the next
daily check.
The installed copy on Windows cannot replace itself, since no user may
write to its folder. A scheduled task named KnightLoader Update does it instead,
once a day and five minutes after the computer starts, whether KnightLoader is
open or not. It replaces KnightLoader.exe, sets the version shown in the list
of installed apps and writes what it did, or why it did not, to
%ProgramData%\KnightLoader\update.log. There is one installation, so the
switch applies to everyone on the computer: it lives in
%ProgramData%\KnightLoader\settings.json, which every user may change, and the
task skips its run while it is off. An open window notices within ten minutes
when the task has put a new version in place and shows the note. Install
now does not wait for the task: it downloads the installer of the new
release, checks it against checksums.txt and starts it. Windows asks for an
administrator, the installer closes KnightLoader and opens the new version when
it is done. The replaced
program waits beside the new one as KnightLoader.exe.old until a later run
removes it.
A portable copy updates itself while it runs: a minute after it starts and once a day after that, in its own folder, and it stays portable. The app on macOS and Linux does the same. A read-only folder, or a macOS app your account cannot change, stays as it is, and the log says why.
Why the task runs as SYSTEM¶
Program Files belongs to the administrators. A program that replaces itself
there without asking anyone needs an account that may write there, and a
scheduled task under the system account is how Windows provides one; Firefox's
maintenance service and Chrome's updater task work the same way. The task
writes to three places and nowhere else: the installation folder, usually
C:\Program Files\KnightLoader; its entry in the list of installed apps, under
HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\KnightLoader; and
%ProgramData%\KnightLoader, for its log. It starts no JDownloader, opens no
window, listens on no port and reads nothing from any user's profile. The folder
under ProgramData belongs to the administrators, and only settings.json in it
is open to other users. The installer gives the installation folder the same
rights as Program Files. That leaves no file a user could swap for one the
system account would run or write to.
You can install into another folder too. Many second drives let everyone
rename what sits at their top, and whoever can rename a folder above the
installation can swap it. On such a drive the task runs as the person who
installed KnightLoader, without administrator rights, and that person may
change the installation folder. Updates arrive the same way; only the version
in the list of installed apps stays at the one first installed. The task downloads only from GitHub over HTTPS and installs a file
only when it matches checksums.txt.
Uninstalling¶
Uninstalling KnightLoader from the list of installed apps asks for an
administrator and removes the program, its shortcuts and its entry, the
scheduled task and %ProgramData%\KnightLoader with the switch and the log. The
webview cache of the person uninstalling, %APPDATA%\KnightLoader.exe, goes as
well. Every user's settings, accounts and private JDownloader in
%APPDATA%\KnightLoader stay, so a later install picks them up, and so do the
files you downloaded.
A container does not update itself. The same page tells you when a newer release exists; pull the new image the way you deployed this one.
KnightLoader itself updates through the update card on the desktop, and through a new image in a container. yt-dlp, JDownloader and the desktop app's Java keep themselves current between releases; their switches sit on the Components card. UnRAR ships inside KnightLoader: a weekly check in the repository looks for a newer one, and each release brings it along. ffmpeg comes with the container image, and the desktop app renews its own copy once a month.
Building it from source¶
The bundle lands in desktop/build/bin; --installer adds the installer for
Windows or Linux in desktop/build/installer, built from desktop/installer
with PommelPorter. It is a
dev build, which never updates itself;
only a build stamped with its version, as the release workflow makes, does (see
desktop/README.md). Windows and macOS need only their usual toolchains. Linux
needs GTK 3 and WebKit: libgtk-3-dev and libwebkit2gtk-4.1-dev to build.
There, install the CLI with go install -tags gtk3 ... as well. Without the tag
the CLI looks for GTK 4, and the error names a missing package rather than the
tag.
The Android app¶
Install it from F-Droid, which keeps it up to date, or take the APK from the latest release, with the same version number as the server beside it. It reaches an instance on your own network or, with the twelve words, from anywhere else: see Connecting instances and apps.
The browser extension¶
Chrome, Brave, Opera and Vivaldi install it from the Chrome Web Store, Edge from Edge Add-ons and Firefox from Firefox Add-ons. Settings, Apps on a running instance has a button for each browser that opens its listing, with the steps beside it:
- Press Add to Chrome (in Edge Get, in Firefox Add to Firefox) and confirm. Opera first asks for its Install Chrome Extensions add-on.
- Pin it. Chrome and Firefox do not put a new extension on the toolbar; it waits behind the puzzle-piece button at the right of the address bar.
- Paste your connection phrase into the Remote access card on the options page, which opens by itself on a fresh install.
A Chromium browser without a store, or a build you want to test, takes the
ZIP from the
latest release:
unpack it, open chrome://extensions, switch on Developer mode and choose
Load unpacked with the unpacked folder.
Firefox installs only add-ons Mozilla has signed, so a build you want to test
loads through about:debugging, This Firefox, Load Temporary Add-on until
the next restart. What the extension does is in
Bookmarklet, extension and share target.
From a checkout¶
The web interface is committed prebuilt in web/dist and embedded into the
binary, so a plain go build gives a working server.