This guide takes you through setting up a fresh Cavil instance for production. It sticks to the essentials: what to
install, how to configure the core settings, and the order in which to bring the pieces up. Every configuration option
is documented inline in the shipped cavil.conf, so this guide points you there for the details
rather than repeating them.
If you just want to try Cavil out locally, the fastest path is the staging scripts described in the README; this guide is for a real deployment.
A working Cavil instance is made of a few cooperating pieces:
- A web application — the Mojolicious app that serves the review UI and the REST/MCP APIs. This is what you point your browser at.
- One or more background workers — Minion jobs do all the real work (downloading, unpacking, license matching, analysis, report generation). The web app only enqueues jobs; nothing happens until a worker runs.
- A PostgreSQL database — the single source of truth for packages, users, patterns, reports and the job queue.
- The curated license patterns — the 28,000+ patterns Cavil ships with, loaded into the database.
- An AI text classifier — a required component that separates genuine license text from scanner noise (see below).
The web app and the workers are the same program (script/cavil) started differently, reading the same config file.
Restart both after any configuration change.
- PostgreSQL with the
pgcryptoandpg_trgmextensions available (any supported release). Cavil enablespgcryptoitself during migration, but the server must provide it — hencepostgresql-contribbelow. - Perl and system dependencies. On openSUSE, install them as packages; the full list is in the
cpanfileand can otherwise be installed from CPAN (cpanm --installdeps .).Cavil::Matcher, the pattern matching engine, is an XS module and needs a C++ toolchain. - Node.js with npm to build the web UI assets.
sudo zypper in -C postgresql-server postgresql-contrib
sudo zypper in -C perl-Mojolicious perl-Mojolicious-Plugin-Webpack \
perl-Mojo-Pg perl-Minion perl-File-Unpack2 perl-HTML-Parser perl-Cpanel-JSON-XS \
perl-Cavil-Matcher perl-Mojolicious-Plugin-OAuth2 perl-Mojo-JWT \
perl-BSD-Resource perl-Term-ProgressBar perl-Text-Glob perl-IPC-Run \
perl-Try-Tiny perl-MCP perl-CommonMark perl-CryptX git git-lfsCreate an empty PostgreSQL database and make sure Cavil's database user can create the pgcrypto and pg_trgm
extensions (or create them ahead of time as a superuser). Cavil creates its own tables in step 4; do not load any
schema by hand.
From a checkout of the repository, after installing the prerequisites above:
npm i
npm run buildCopy the shipped cavil.conf as your starting point and edit it. Cavil reads whichever file you point it at with the
CAVIL_CONF environment variable. Every option is commented in that file; the ones you must set for a fresh
instance are:
secrets— a list of your own random strings used to sign session cookies. Never keep the default.pg— the connection string for the database from step 1 (e.g.postgresql://user@/legaldb).checkout_dir— a directory on a large disk where unpacked source is kept for reindexing.tmp_dir— a directory for incoming files before they are moved intocheckout_dir.cache_dir— a directory for temporary files shared between indexing processes.classifier— the AI classifier connection (see step 6).openid— OpenID Connect settings for real user login. If omitted, Cavil falls back to a dummy login that makes everyone an admin, which is fine for a first boot but must not be left on for a shared instance.
To connect Cavil to your sources (Open Build Service, Gitea, or the bot API), also set the obs, git,
external_link_sources and tokens options — all documented in cavil.conf. These can be added later.
In production mode Cavil also picks up a mode-specific config file next to your main one automatically — a
cavil.production.conf beside cavil.conf is merged on top of it — which is a convenient place to keep
production-only overrides separate from shared settings.
Cavil does not migrate automatically. Run it once now, and again after every upgrade:
CAVIL_CONF=/path/to/cavil.conf script/cavil migrateAn empty instance can't recognize any license. Load the curated set:
CAVIL_CONF=/path/to/cavil.conf script/cavil sync -i lib/Cavil/resources/license_patterns.jsonlThe keyword scanner has a false-positive rate around 80%; the classifier is what tells real license text from that noise, and all of Cavil's automated snippet resolution builds on its verdict. Without it you get raw keyword matching drowning in false positives, so a classifier is required for a real instance.
The openSUSE HuggingFace org publishes models fine-tuned for this task (e.g.
Cavil-Qwen3.5-4B), best served with a llama.cpp server (a GPU helps; it can
run on the Cavil host or a separate machine):
llama-server Cavil-Qwen3.5-4B.f16.gguf --host localhost --port 5000 --api-key TOKENThen add the matching block to cavil.conf:
classifier => {
type => 'llama_cpp',
url => 'http://localhost:5000',
token => 'TOKEN'
}Cavil runs classification automatically as new snippets come in, so there is nothing else to schedule. The only data sent to the model is the raw candidate snippet, and embargoed packages are never sent to the classifier (this exclusion is enforced in Cavil itself for compliance; do not work around it).
Cavil also still speaks the older classifier API, if you would rather run one of the legacy implementations
(Character-level-cnn-pytorch or
llm-lawyer). Omit the type key from the classifier block in that case.
Run the web application with the prefork command in production mode, and start at least one worker. Both read
CAVIL_CONF:
CAVIL_CONF=/path/to/cavil.conf script/cavil prefork -m production --proxy -l http://*:4000
CAVIL_CONF=/path/to/cavil.conf script/cavil minion workerUse prefork rather than Hypnotoad here: Hypnotoad is built for signal-driven zero-downtime reloads and only reacts to
a subset of signals, which does not fit a systemd-managed service well. Run each process as its own systemd unit. A
typical ExecStart for the web app, tuned to the host, looks like:
ExecStart=/path/to/cavil/script/cavil prefork -m production --proxy -w 20 -c 1 -i 100 -H 900 -G 800 -l http://*:4000
The flags let you size and tune the server (adjust the numbers to your hardware):
-m production— production mode (short, systemd-friendly logs).--proxy— trust the reverse proxy's forwarding headers, so client addresses and HTTPS are seen correctly.-w— number of worker processes;-c— maximum concurrent connections per worker.-i/-H/-G— inactivity, heartbeat and graceful-shutdown timeouts in seconds; generous values suit long-running requests such as large uploads and report generation.-l— the address to listen on.
Run the worker as its own systemd unit alongside it. Its ExecStart is simply the minion worker command, with -j
setting how many jobs it processes in parallel (size this to the host's CPU and memory):
ExecStart=/path/to/cavil/script/cavil minion worker -m production -j 22
For a real deployment:
- Put a reverse proxy in front of it (strongly recommended). Run nginx (or similar) in front of the app to terminate TLS and forward requests, and keep the application port off the public network (firewall or a loopback bind).
- Run them as services under a dedicated user. Run the web app and worker(s) as separate systemd units owned by an
unprivileged user that owns the checkout,
checkout_dirand config. Size the worker count and worker job concurrency to the host's CPU and memory (max_worker_rssandmax_task_memorycap their memory use). - Plan for disk and backups. The
checkout_dircache is the largest, fastest-growing consumer of space, so put it on a large volume and keep the cleanup task scheduled. Back up the database regularly and test a restore; patterns can also be exported withscript/cavil sync -eas an extra copy.
Users are created on first login, so you can't grant a role to someone who hasn't signed in yet:
- Start the web app and log in once as the future administrator (with OpenID Connect this creates a
user-role account). - List users to find its id, then grant the
adminrole:
CAVIL_CONF=/path/to/cavil.conf script/cavil user
CAVIL_CONF=/path/to/cavil.conf script/cavil user -A admin <id>Roles are capability bundles: admin runs the instance and curates patterns, lawyer curates and carries the legal
sign-off, manager signs off as a non-lawyer expert, contributor proposes patterns, and classifier classifies
snippets. Use -A/-R to add and remove them; the full capability matrix is in the
Architecture guide.
Your instance is now ready to review packages. To feed it work, connect the OBS bot or the Gitea bot, upload tarballs directly from the UI, or use the REST/MCP APIs documented in the Bot API and User API guides.
For ongoing care — reindexing after pattern updates, cleaning up obsolete reports, and retrying failed jobs, all via Minion's built-in scheduler — see the Maintenance guide. For how Cavil works internally, see the Architecture guide.