shelltrap.com
en de

Documentation

Licensing and activation

One licence per server, verified offline against a signed token. The licence service does not have to be reachable for the scanner to work — but it does have to be reachable to renew.

The pieces

TermMeaning
Licence keySTL-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX — Crockford Base32, 25 payload characters including two check characters. Generated when the licence is created and returned in clear text exactly once; the service stores only sha256(key) plus the first nine characters as a display prefix.
LicenceThe record: plan, maximum servers (1 by default), status (active, suspended, terminated), expiry, and the link to your account.
ActivationBinding a licence to a server fingerprint. At most max_servers at a time; deactivate frees a slot.
Licence tokenAn Ed25519-signed document the daemon verifies offline.
Server fingerprintsha256 over a fixed prefix and the contents of /etc/machine-id. The hostname is sent for information only.

Activation

shelltrap license activate STL-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
shelltrap license status
shelltrap license renew
shelltrap license deactivate

The token lands in /etc/shelltrap/license.token, root, mode 0600. The key itself is stored only as a prefix in /etc/shelltrap/license.key, also 0600, so renewals do not need you to type it again. If you would rather not have the key on the server at all, delete that file and renew manually.

A shelltrap-license.timer runs the renewal daily with up to an hour of jitter. That service is a separate systemd unit from the daemon, because it is the only component that needs AF_INET or AF_INET6 at all — shelltrapd itself is restricted to AF_UNIX.

root@web1 — /root
$ shelltrap license activate STL-7QK3M-P2VDA-XN94T-B6HRE-Z8SWF
plan             server
key prefix       STL-7QK3M
valid until      2027-09-04
token until      2026-10-04
fingerprint      e3b0c44298fc1c14…
servers          1 of 1
token written to /etc/shelltrap/license.token (0600, root)
$ shelltrap --json health
{"status":"ok","watcher_tier":"A","scanner_workers":2,"clamd":"ok",
 "ruleset_gen":"20260903T120000Z-0001","policy_gen":17,"queue_age_s":0,
 "overflows":0,"feeds":"ok"}

Activation, then a health check. Fields shown are the ones the CLI documents: plan, prefix, validity, token validity, fingerprint and server quota.

What is inside the token

The payload is canonical JSON with sorted keys. The fields that matter operationally:

  • not_after — token validity, 30 days after issue. After that the token is invalid even if the licence itself runs longer. This is what forces renewal.
  • license_expires_at — paid until, plus 14 days of grace. If it falls before not_after, the earlier date wins.
  • fingerprint — the server this token is for. A token moved to another machine does not work.
  • features — the capability list the licence unlocks.
  • key_id — which signing key to verify against.

Verification uses pinned licence public keys. The package ships the Panomity licence public key at /usr/share/shelltrap/keys/license-panomity.pub, and configuration may only add keys, never replace that pin. That is deliberate: it stops a server operator from swapping the trust anchor.

The three states

StateWhenWhat the daemon does
licensedvalid token, matching fingerprintnormal operation
gracetoken expires in under 7 days and renewal is failingnormal operation, plus a warning in the log, health degraded with license_renewal_failed, and one admin notification a day
unlicensedno token, bad signature, expiry passed, or a foreign fingerprintsee below

In unlicensed the daemon does not crash and does not restart-loop:

  • the watcher keeps running and events are counted but not scanned,
  • the scheduler plans nothing,
  • the upload gate answers allow with reason unlicensed,
  • health reports status: "unlicensed" with a precise degraded_reasons entry — license_missing, license_expired, license_invalid or license_fingerprint_mismatch,
  • an audit entry license.state is written,
  • the metrics shelltrap_license_state{state=…} and shelltrap_license_expires_seconds reflect it,
  • the feed service answers 401 and the feed controller reports feeds: unlicensed.

The design goal there is worth stating: an expired licence must be loudly useless, never quietly dangerous. A gate that started denying uploads because an invoice was late would be a worse product than one that says so in health and lets traffic through.

Feeds and the licence

Every feed fetch sends the compact token as a bearer credential. Without a valid token the service answers 401. Signatures, updates and support are the commercial boundary of this product — see signature feeds for what arrives when the token is good.

Downloads

GET /v1/downloads/index.json is readable without a licence, so a website can display the current versions. The packages themselves are served only against an active licence: GET /v1/downloads/<file>?key=STL-…. The service logs the key prefix, never the key. There are no unauthenticated package links, which is why the download page has a form instead of a list of URLs.

Moving a licence to another server

# on the old machine
shelltrap license deactivate
# on the new machine
shelltrap license activate STL-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX

Re-activating a fingerprint that is already active simply issues a new token and does not consume a second slot, so a renewal never locks you out of your own server.

What a licence check cannot do

An attacker with root on your server can patch any binary on it, ours included, and no client-side check survives that. We say so in the licence terms as well. The commercial protection rests on signatures, updates and support being reachable only with an active licence, and on no source code being delivered.

Next

Abridged from the Shelltrap licensing manual, revision 2026-09-04.