kineuroExperimental Neuroradiology Research
Karolinska Institutet

Install and get started

One command, a few questions, and a working NILS. Everything else in this documentation is what those questions do.

On Linux or macOS:

curl -fsSL https://nils.kineuro.se/get | sh

That puts one small binary on the machine and hands over to nils setup, a wizard that asks what you want, tells you what this machine can do, and installs and runs the rest.

Every question comes first, and nothing is written before it shows you the plan. In a terminal each step has a screen of its own, with the steps down the side and your answers beside them. The arrow keys choose, Enter goes on, and Left or Esc goes back to change an earlier answer, which is offered again as you left it. Once you say yes, there is only work to watch: a checklist of what the plan does, ticked as each part is done, then a card with where each part answers and whether its service runs. Without a terminal, or with --yes, it asks nothing and takes every default.

What it asks

1. What to install. The engine alone, the engine and the desk, or everything. The engine is the registry itself and works on its own from a command line; the desk is the web application over it; the assistant turns words into questions and needs a model, which it reaches through Kvasir, the model gateway.

2. Where it runs. On the machine, in podman containers, or in docker containers, whichever this machine has. Docker is offered when its daemon answers and this account may use it; when it does not, the wizard says why. In containers the engine and the desk use their published images. Kvasir and the assistant are built on the machine, which needs Node 22, and run in Node’s image beside them. Only the desk and Kvasir are published, each on this machine’s loopback unless you open the desk to the network.

3. Where it lives. One directory, ~/nils by default, holding the registry, the desk’s configuration, the backups and the working space. It also asks for the folder your dataset folders are in, if you have one to hand. That becomes the engine’s source place, a root, and setup adds the root alone: a folder under it becomes a dataset only when you add it, and only then is its structure read (Add a dataset, or nils place add-dataset). On a rerun it offers the folder it reads now, and a root added later, at the desk or with nils place add, is mounted and given to the engine as well.

4. The registry. SQLite by default. Or Postgres, set up here: the wizard runs Postgres 17 in podman or docker beside the other parts, with its data in ~/nils/postgres and a generated password kept readable by you alone; it is offered when podman or docker is on the machine. Or a Postgres you already run, given by its connection string, which the wizard tries at once. In containers, a Postgres you run on this machine is reached the way a model server is: podman hands the pod this machine’s loopback, and with docker Postgres must listen on docker’s bridge and allow it in pg_hba.conf. Then a passphrase for the registry’s key, asked twice and never shown. Pseudonyms are derived from that key: the same subject under the same key gets the same code, so keep the passphrase where you keep passwords.

5. Who may sign in. Nobody, one person on this machine. Or the desk keeps the people and their passwords. Or an identity provider decides. One answer sets both the engine’s authentication and the desk’s mode, and it asks who may reach the desk: this machine only, the network, or a proxy of yours that answers at a name of its own (--origin URL). Choosing the network with no login gets a plain warning, because anyone who finds the port would have the whole registry. When the desk keeps the people, it offers to add the first one now: a username, and a password asked twice and never shown. The username is letters, digits, dots, dashes and underscores, and the password is at least eight characters, as the desk holds them. That person joins Admins, and may do everything. When an identity provider decides, it asks where the desk is registered: at an Authentik, where the wizard registers it with an API token and two groups, one whose members use NILS and one of those who run it; or at a provider already, given the issuer, the desk’s client id and its secret. --authentik URL --authentik-token-file FILE, or --oidc-issuer URL --oidc-client-id ID --oidc-client-secret-file FILE, does the same without asking. Where people sign in, the desk signs a token for each person with what their groups and grants give, and the engine and Kvasir trust the desk, beside the provider when there is one.

6. What it can do. It reads the graphics card and says what that means before offering the assistant:

The card What you can serve
24 GB or more a 27B model at 4 bit, with room for the context; what the stations were written against
12 to 24 GB a 7B to 14B model; the stations still work, with more retries on the harder questions
less, or none no local model worth serving

If you install the assistant, it then asks what the assistant should talk to. The assistant reaches its model through Kvasir, over the OpenAI chat API, which almost every model server and provider speaks.

Choose When What to type
A model server on this machine SGLang, vLLM, llama.cpp or Ollama is already running here its address: SGLang listens on http://127.0.0.1:30000/v1, vLLM on :8000/v1, llama.cpp on :8080/v1, Ollama on :11434/v1
A model server on another machine of yours the same kind of server, reached over your network its address as this machine reaches it, such as http://192.168.1.20:30000/v1; the prompt stays inside your own systems
A commercial provider you have an account with one its address, such as https://api.openai.com/v1 or https://openrouter.ai/api/v1, and your key, which is never shown; the prompt leaves your systems
Your ChatGPT subscription nobody signs in, and you have a ChatGPT subscription nothing now; once Kvasir runs, a link and a code to sign in with; the prompt leaves your systems
Decide later you have no model yet nothing; run nils setup again when you do

If the address answers, the wizard asks the server which models it serves and you pick one from the list. If nothing answers yet, it asks for the model’s name as the server lists it. A model is taken only once it answers one short question: otherwise the wizard says why, nothing answered, the key was refused or there is no such model, and asks again or leaves it for later.

Once Kvasir runs, the model is added to it, and Kvasir tries it again from where Kvasir runs, holding it only once it answers. A model on a server of yours is then warmed and run through Kvasir’s admission, which Kvasir does on its own while setup waits, since Kvasir offers no local model it has not admitted. With a commercial provider or the ChatGPT subscription, questions that read your registry stay refused until someone with work on Kvasir chooses to let its rows leave your systems; see the model and Kvasir. The questions that read no rows go to it at once. Running nils setup again says what the assistant talks to and asks whether to keep it.

The ChatGPT subscription is offered where nobody signs in, and it is then the install’s own: setup shows a link and a code, and waits while you open the link, sign in with ChatGPT and enter the code. Where people sign in, a subscription is each person’s own: once a station goes to ChatGPT, a person who may use the assistant and see Kvasir adds theirs there.

With containers, Kvasir reaches a model server on this machine differently. With podman it is handed this machine’s loopback, so a server listening on 127.0.0.1 works as it is. With docker it reaches this machine over docker’s bridge, so start the model server on 0.0.0.0 or on the bridge’s address. The wizard says which applies when you choose a server on this machine, and a model Kvasir cannot reach from its container stops the install with Kvasir’s own words.

Without a card the realistic choices are a model on another machine, a provider, the ChatGPT subscription, or no assistant at all, which costs nothing else: the engine and the desk are complete without it.

With the assistant, the wizard also installs llama.cpp’s server, pinned to one build and checked against its published sha256, and runs it beside Kvasir with no model loaded: the Vulkan build on Linux where it finds a graphics card, the processor build where it finds none, and the Mac build on a Mac. The plan names the build and the devices it sees, and says when Linux lacks the Vulkan loader (libvulkan1), since a model then runs on the processor. Kvasir downloads a model’s files into a folder you choose, and a downloaded GGUF model is started on the desk’s Language models page, where models are also added, checked and removed; see the model and Kvasir.

7. Keeping it running. Systemd user units, podman quadlets, a compose file, or launchd agents, whichever suits the machine, so everything comes back after a restart. For a machine that serves a group, --system writes the machine’s own services, each part under an account you name; the setup guide lists the flags.

8. The plan. Every path, port and choice on one screen, including the first person and the model. Then it does the work, starts the services in order, and marks on the card which are running. If one isn’t, it shows the line of that service’s log that says why. An install that would not work, such as the desk not installed, a model Kvasir did not take or a service that does not start, stops with the reason, and nils uninstall removes what was placed.

After it finishes

If you installed the desk, open http://127.0.0.1:7200. On a fresh install Home shows the steps that make it ready for real work: a root folder, a backup place, who signs in, and a model for the assistant (Get started). Then add a dataset on Data and read it.

The same on the command line:

nils place folders source            # the folders under the root
nils place add-dataset source FOLDER # add one; its structure is read now
nils bring-in @FOLDER                # read it, sort it, pick its main scans
nils status                          # what the registry holds

Doing it again, and keeping it current

nils setup           # says what you have, and offers to change or repair it
                     # repair also adds the model to Kvasir, makes the assistant's key and mends Kvasir's settings
nils update --all    # takes the newest nils first, which then updates every other part it installed
nils update --check  # every part beside its own newest release, and what --all would take
nils update --part desk   # one part alone: engine, desk, assistant or kvasir

The engine, the desk, the assistant and Kvasir release on their own, each under its own version number, so an update is offered whenever any part is behind its own newest release. The engine and the desk are paired by their contracts, not their numbers: every desk release publishes a contracts.json with the engine contracts it speaks, and the update takes the newest desk release whose floor the engine meets. A newer desk that needs an engine contract the engine does not speak yet is said to wait and is not installed; it comes with the engine release that speaks it. The assistant and Kvasir move to their own newest tags, never below the tags this nils pins. NILS_SETUP_DESK_VERSION pins the desk at one version, for a lab.

Both know what you installed and how it runs:

  • A binary is replaced in place.
  • A container takes the new image, the desk’s by the desk’s own version.
  • The packs of the engine’s release go into the directory the engine reads them from, where only packs that differ move, the ones they replace are kept in <dir>.previous, a site’s own packs are never touched, and a first-party pack changed on the machine is kept and said. This happens even where the engine is already at its newest release, so an install left behind catches up.
  • The rules also ship on their own tag (pack-mri-v<version>), so a rules fix reaches an install with no engine release. nils update --part rules takes the newest one the engine reads and restarts nothing. nils update --check lists each pack beside its newest release.
  • Kvasir and the assistant are fetched at the release tags this version of nils names, checked out and rebuilt.
  • llama.cpp is fetched at the build this version of nils pins, checked against its sha256, and its service restarted; Kvasir loads the model it had started again.

nils update --all replaces nils itself first and starts the new binary to update the rest, so every part moves to its newest release that fits. A nils older than 1.0.0-alpha.27 did it the other way round and updated the parts with the versions it named itself, a release behind; after updating from one, run nils setup once.

When anything changed, every service is restarted in order and it says which are running; when nothing did, nothing is restarted.

A change keeps what it can: the registry, the desk’s people, the models Kvasir holds, and the keys you set yourself in the desk’s configuration. An install from before Kvasir held its models has them taken out of kvasir.json and added back through Kvasir, with their keys. A desk that comes to keep its own people with nobody in it yet gets its first person asked for, and a change of how people sign in signs everyone out of the desk once; see the desk keeps the people.

A desk updated from a release before groups makes the groups Readers, Reviewers, Operators and Admins, and puts each person it keeps in the group of their old step; see groups and grants. Where people sign in, the update also mends Kvasir’s trust to match the desk’s, and a desk that signs people in through a provider signs everyone out once.

Removing it

nils uninstall              # asks which of the two, shows what goes and what stays, then asks again
nils uninstall --print      # only shows it

It is also the last choice in the nils setup menu, and, where a supervisor keeps the install, Uninstall NILS at the foot of the desk’s Settings, Setup, for a person with work on the install (uninstall NILS). There are two ways to leave:

Choose Removes Keeps
NILS, keeping your data (--keep-data) the services, the programs, the first-party packs, what building Kvasir and the assistant made, the llama.cpp build and its service, Kvasir’s directory with the models it holds, their keys, its subscriptions and the assistant’s key, and the setup record the rest of the base directory: the registry and its key, a Postgres the wizard set up with its data, the backups, the desk’s database with its people, and the assistant’s conversations
Everything (--purge) all of that, and the base directory itself nothing that setup made

To remove everything, you type the base directory’s name to confirm, because nothing brings the registry’s key back: without it the same subject can never be given the same code again. A wrong name changes nothing. The desk asks for the name either way, and the supervisor checks it again, records who asked, and goes on after the desk is gone.

Without a terminal it asks nothing and needs --yes to act, and on its own that means keeping the data. A setup that stopped partway is removed the same way. With no record at all, nils uninstall lists what an unfinished setup left in the usual places and removes it when you confirm. --purge --yes removes everything without asking. Use it only in a script that means it.

Some things it leaves alone whichever you choose:

  • Programs it didn’t install. It removes only the programs the setup record names, itself included. A nils you run from somewhere else stays, and the plan says so.
  • Your own packs. A pack directory that also holds packs of your own keeps them.
  • Directories an install didn’t make. It refuses to remove a base directory that isn’t absolute, that is the root or holds your home directory, or that holds neither a registry nor a desk an install made.
  • Other docker compose projects. On docker it removes only its own two containers, never a whole compose project that might share its name.

After keeping the data, installing again into the same directory picks it up where it was:

curl -fsSL https://nils.kineuro.se/get | sh -s -- --dir ~/nils

The wizard finds the registry and leaves it alone, and asks for the assistant’s model again.

Starting again from an empty registry

A site that has tried NILS out and now starts its real work keeps its setup and starts its registry again. The configuration, the sign-in, the desk’s people and groups, and Kvasir’s models stay as they are. Nothing is deleted: the old registry is set aside, and opens again with nils --registry <that folder>.

nils --registry <registry> place export --out places.json
nils setup --new-registry --reg-key-file KEYFILE --print
nils setup --new-registry --reg-key-file KEYFILE --yes
nils --registry <registry> place import places.json --print

The new registry has no jobs, no data, no ID types and no places but the setup’s. place export writes every place in force, a dataset with how it is declared, and place import declares them again in the new registry: it looks into no folder and moves nothing. The new registry numbers its pipeline runs from 1 again, so a new run that finds an old run’s folder of the same number moves it aside whole (runs/<id>.before-<when>), never deleting it, and says so.

Without the wizard

Everything it does, you can do yourself, and the rest of this documentation is exactly that: the engine on its own, the desk and the three ways people sign in, and the assistant with Kvasir. Read those when you want to know what a step did, or when your machine is not one the wizard covers.

What the machine needs

The wizard checks these and says what is missing, before anything is placed.

For What
anything curl
containers podman or docker
the assistant Node 22, git and npm, and a model to talk to
a graphics card for local models on Linux the Vulkan loader, libvulkan1 on Debian and Ubuntu
building from source Rust, a C compiler, git; Node 22 as well for the desk
releases to NIfTI or BIDS dcm2niix; setup places the build it pins beside nils, and nils tools install dcm2niix or Settings, Parts, Tools, Install does it later. Where the machine cannot reach GitHub, --from FILE or Install from a file takes the archive fetched elsewhere; see dcm2niix

Windows is not covered by the wizard yet; the engine builds and runs there, and the engine’s pages say how.

Updated 17 October 2026