Click'n'Load¶
Click'n'Load ("CnL") is how a website hands a batch of links straight to a download manager. It is a de-facto standard rather than a specification: it is whatever JDownloader implemented, and every site that offers a "Click'n'Load" button talks to that. KnightLoader answers the same protocol, so those buttons work without the site knowing or caring which program is listening.
How a site actually does it¶
Three steps, all aimed at 127.0.0.1:9666, the port JDownloader has listened on
for two decades, and which every CnL button hard-codes.
1. Detection. The page loads
and checks whether the global jdownloader came out true. KnightLoader answers
with jdownloader=true; so the button appears.
2. Submission. Clicking posts a form to /flash/addcrypted2:
| Field | Contents |
|---|---|
crypted |
the link list, AES-128-CBC encrypted, base64 encoded |
jk |
a one-line JavaScript function returning the hex key; the key doubles as the IV |
passwords |
archive passwords for the download, newline separated (optional) |
source / package |
where the links came from, used as the package name (optional) |
The encryption is not security, because the key travels with the ciphertext. It exists so a link list is not sitting in plain text in the page source for a scraper to harvest.
KnightLoader extracts the hex key from jk with a regular expression rather
than executing the snippet. jk is attacker-controlled JavaScript from an
arbitrary website; running it would be an obvious way to lose the machine.
3. Plain variant. Older or simpler sites post an unencrypted list to
/flash/add with a urls field.
4. addcrypted (v1). The oldest variant, from before addcrypted2 existed:
the site posts a crypted field encrypted against JDownloader's own RSA
public key rather than a key that travels with it, so nobody but a real
JDownloader can open it. KnightLoader does not hold that key and never will.
See "Container files" below for the reason behind that choice, and for how it
is answered anyway when a JD backend is configured.
Every one of these four accepts POST only. A GET here would be a browser
"simple request" (no preflight, no user gesture, no navigation), so
any page in the world, an ad iframe or an email preview image tag included,
could queue arbitrary downloads and archive passwords into this instance. A
handful of read-only routes some sites and extensions probe before ever
trying to submit (/flash/addcnl, /flashgot, /alive, /favicon.ico,
/crossdomain.xml, alongside jdcheck.js and the bare detection paths above)
answer GET only and never touch a link or a password.
The three things that stop it working¶
Private Network Access¶
Chrome will not let a page on the public internet open a connection to a private
or loopback address unless the target opts in. The browser sends an OPTIONS
preflight first and expects Access-Control-Allow-Private-Network: true back.
Without that header the failure is quiet in the worst way: sites that submit
with a plain HTML form still work, while sites that use fetch or XHR fail
with nothing in the UI to explain it. KnightLoader answers the preflight.
Cross-origin rules¶
A page at https://example.com posting to http://127.0.0.1:9666 is a
cross-origin request, so the CnL routes send Access-Control-Allow-Origin: *.
That wildcard is confined to the CnL listener. The protocol requires it, since
any site must be able to reach the listener, and the listener binds
127.0.0.1 only, so nothing off the machine can talk to it. The main API on port 8749 does the exact opposite and
refuses foreign origins outright. Do not "fix" one to match the other.
Mixed content¶
An HTTPS page loading http://127.0.0.1:9666/jdcheck.js looks like mixed
content, which browsers normally block. Loopback is the exception: both Chrome
and Firefox treat 127.0.0.1 and localhost as potentially trustworthy, so the
script tag is allowed. You do not need to do anything here, but it explains
why the address must stay literally 127.0.0.1 rather than a hostname that
happens to resolve there.
When KnightLoader is not on your desktop¶
This is the case that matters for the primary deployment, and the one that plain
CnL cannot solve: the container runs on a NAS, and 127.0.0.1 inside that
container is not the 127.0.0.1 your browser means. The site posts into the
void.
Publishing port 9666 does not help. The address is hard-coded in the site's JavaScript; it will never dial your NAS.
There are two answers, and the browser extension is the one most people want.
The browser extension. It catches the submission inside the page, before it is ever sent, then hands the links to whichever instance you pick, the same chooser every other send from the extension uses. Nothing runs on your desktop, no port is owned, and it works wherever the instance is. It is what most people install the extension for, so the options page that opens after the install leads with it.
This looks impossible at first, since an extension cannot listen on a TCP port
and so never receives the POST. Instead it patches the page's own fetch, XHR
and form submission at document_start and takes the payload before it leaves
(extension/src/cnl-main.js), decodes it in the browser
(extension/src/cnl.js, the same AES-128-CBC this file describes), and answers
the site with the identical success\r\n a real listener would. The detection
step is answered the same way: the interceptor declares jdownloader = true
before any script the page brings, so the button appears. The page cannot set
it back to false. filecrypt's helper window does exactly that and then waits
for jdcheck.js to set it again, but its own Content-Security-Policy blocks
that script before the redirect ever sees the request.
That means running code in every page you visit, which is a real permission.
The manifest lists <all_urls> under optional_host_permissions, so the
install dialog asks for no website access, and the browser asks for it when
Click'n'Load is switched on. The switch shows whether the interception runs,
not whether it is wanted, so it never reads "on" while the browser still
withholds the access. A fresh install opens the options page with a card that
asks for it in one click.
Switching it off really switches it off, which is the part a static
content_scripts entry could never offer: the two scripts are unregistered
through chrome.scripting.unregisterContentScripts, and
chrome.scripting.getRegisteredContentScripts() returns []. Nothing of this
extension runs in any page until it is switched back on. (JDownloader's own
extension requires the same access and registers eight content scripts fixed in
its manifest, which cannot be switched off at all.)
The bridge. This is for someone who wants no extension at all, or a browser
without one. Run the same binary on your own machine in bridge mode. It
listens on 127.0.0.1:9666, speaks CnL to the website, and forwards what it
decodes to the remote instance over the normal REST API:
knightloader -bridge http://nas:8749
# with a password-locked instance:
knightloader -bridge http://nas:8749 -bridge-password 'your-ui-password'
# with an instance a reverse proxy serves under a path:
knightloader -bridge https://example.com/kl
It downloads nothing itself and needs no data directory. It is a few hundred kilobytes of forwarding. The remote does the work, and the links land in its collector exactly as if you had pasted them.
The desktop build needs none of this: it is the server, running on your machine, so its own CnL listener is already the one the browser means.
Configuration¶
| Var / flag | Default | Meaning |
|---|---|---|
KL_CNL |
9666 |
listener port on 127.0.0.1; 0 disables it. A container opens it only when this is set |
-bridge-clipboard |
off | bridge mode only: watch the OS clipboard for hoster links (needs a -tags bridgeclipboard build) |
In the container the listener stays down unless KL_CNL is set. Its
127.0.0.1 is the container's own, which a browser reaches only when the
container shares the host's network, so the switch in Settings, Link collector
is greyed out there and its (i) points to the browser extension and the desktop
app. Set KL_CNL=9666 on a container with host networking to open it anyway.
It stays bound to 127.0.0.1. The Click'n'Load protocol has no authentication,
so binding it to the LAN would be an open "add these links to your downloader"
endpoint for the whole network.
Two KnightLoaders on one computer¶
One computer has one port 9666. When the desktop app and a server run as a
plain program share a computer, the one that starts second finds the port
taken. Its switch under Settings, Link collector names the KnightLoader that
holds it, which answers who it is on 127.0.0.1:9666/knightloader (a route
without CORS headers, so no website can read it), and offers Take over. That
asks the holder through the group to let go of the port and then listens here.
A KnightLoader outside the group cannot be asked, and a program that is not
KnightLoader, usually a running JDownloader, is named as such and has to be
closed by hand. The browser extension never holds the port: it catches
Click'n'Load inside the page, so there is nothing to take over from it.
Checking it by hand¶
With KnightLoader (or a bridge) running:
curl -s http://127.0.0.1:9666/jdcheck.js
# jdownloader=true;
curl -s -X POST http://127.0.0.1:9666/flash/add \
--data-urlencode 'urls=https://example.com/file.bin' \
--data-urlencode 'package=Manual test'
# success
The links appear in the collector. If jdcheck.js answers but a real site's
button does nothing, the preflight is the first thing to look at: open the
browser's network panel and check whether the OPTIONS request came back with
Access-Control-Allow-Private-Network.
Container files, and addcrypted (v1)¶
A site's addcrypted (v1) submission is a .dlc in all but name, and
KnightLoader opens both itself (see internal/container's package doc).
Handing KnightLoader a .dlc (POST /api/containers) and a site sending
addcrypted take the same path. A .ccf or .rsdf opens here too.
A file that does not open here goes to the headless JDownloader backend that
KnightLoader already ships as its catch-all resolver. An addcrypted payload is
handed to JD as inline content instead of a fetchable URL, because unlike an
uploaded file it was never a file anywhere to fetch. A build made from source
opens no .dlc itself, since the key it needs is added only when a release is
built (see docs/decisions.md), so there every .dlc takes this path.
Without KL_JD configured, a container that does not open here is refused
with an error that states that reason. See the main README's configuration table.
Ambient clipboard watching (bridge only)¶
The bridge can also watch the OS clipboard and forward anything that looks like a hoster link, without waiting for a CnL button or an explicit paste, which is useful for a site with no Click'n'Load button at all. It is off by default:
This is scoped to the bridge specifically because it is the one build with an
unambiguous claim on a user's own OS clipboard: the user started it, by hand,
on their own machine. It also needs a build with -tags bridgeclipboard: the
ordinary release binary (and the container image) does not carry the
clipboard-reading dependency at all, because a headless server has no
legitimate reason to touch a clipboard. Passing -bridge-clipboard against a
plain build logs that the build has no clipboard support instead of silently
doing nothing.
Watching is narrow: only a line that is essentially just a link qualifies, not prose that happens to contain one, because nobody is watching the result to catch an accidental selection before it queues. A small ring of recently-forwarded content is kept so the same clipboard is not resubmitted on every poll tick, and so KnightLoader's own "copy link" writing a link back into the clipboard does not get read in as if it arrived from somewhere else.
What CnL does not cover¶
Right-click-send-to-KnightLoader on an arbitrary link is a browser extension's
job, not CnL's: CnL only exists where a site chose to put a button. See
docs/browser-tools.md for that extension, the bookmarklet, and the PWA
share target.
The extension now covers both: the right-click entries, which have nothing to do with this protocol, and Click'n'Load interception, which is entirely this protocol. They remain separate features, and the site access belongs to the second one alone: the right-click entries and the toolbar button keep working with Click'n'Load switched off and its content scripts gone.