Skip to main content
This is the same standalone auth server as the self-hosted quickstart, delivered without Docker: an archive with a self-contained executable plus an installer that registers it as a systemd unit or a Windows service. The target machine needs no .NET runtime. What this page covers is the installation — where the files live, how the service is registered, how it is updated and removed. What to put into the configuration (stores, certificates, clients, channels) does not change between delivery modes and stays in the self-hosted quickstart.
The installer leaves you with a running service, not with a production one. It writes a start-up configuration — an in-memory store and two self-signed token certificates — so that the service answers /health/live straight away instead of refusing to start. Everything that makes it a production instance is step 6.

1. Download the archive and check it

A release carries three archives and one checksum file: They are release assets, and the base of their address is stable — permalink/latest always resolves to the newest release:
The file name carries the version, so the address as a whole is not a permanent link: once the next release is out, a path with the previous version is no longer among the assets permalink/latest resolves to, and the download answers 404. Take the repository address and the current version from veriqa.app/source each time instead of keeping a ready-made URL:
--ignore-missing is what makes this work with a single archive downloaded: the sums file lists all three, and without the flag sha256sum fails on the two you did not take. On Windows:
True — the archive is intact.

2. Install on Linux

The archive unpacks into a single veriqa-authserver/ directory and carries its own installer, install.sh. Run it as root from that directory:
The installer creates the system user veriqa (no login shell, no home directory created), copies the program, creates the configuration and data directories, generates the token certificates, writes the start-up configuration, installs the unit file and enables the service. It does not start it: the start-up configuration is meant to be read before the first start, not after it.
The program directory belongs to this installation alone. Both installers write into it without clearing it out first — a file of the archive overwrites its counterpart, anything else stays where it is — and removal is the mirror image: --uninstall / -Uninstall takes away the files the installer copied and nothing else, and keeps the directory itself if something else is still in there. Point --install-directory / -InstallDirectory at a directory of its own and the installation comes off in one step.
The default --urls is loopback only — the instance is reachable from the same machine and from a reverse proxy on it, and from nowhere else.
TLS is needed for the API itself, not only to open the instance up. Over plain HTTP the token endpoint answers 400 invalid_request — “This server only accepts HTTPS requests” (ID2083) — so a backend calling the confirmation API gets no token, even from the same machine. Do step 7 before the first call: put a reverse proxy in front, or give Kestrel a certificate.
Start the service when you have read what the installer printed:
The installer refuses to run over a service that is already registered under the same name (use --uninstall first) rather than recreate it silently. Updating an existing installation is Updating.

3. Install on Windows

Unpack the .zip and run install.ps1 from the unpacked directory in an elevated PowerShell (Windows PowerShell 5.1 and PowerShell 7 both work):
The service is registered with automatic start under the virtual account NT SERVICE\<ServiceName> — no account and no password of your own. The configuration directory is %ProgramData%\Veriqa; its access list is rebuilt without inheritance, so the Users group gets no access to the secrets kept there. As on Linux, the service is registered but left stopped:

4. What the first start gives you

The installation is deliberately complete enough to start and no more:
  • the OpenIddict store is InMemory — registered clients, issued tokens and the journal are lost on every restart;
  • the token certificates are self-signed, RSA-2048, valid for two years, and were generated on this machine;
  • no channel and no OIDC client is configured, so a real sign-in cannot complete yet.
Both health endpoints answer 200 in this state:
/health/ready is 200 because the start-up configuration declares no external dependency to check — not because the instance is ready for production.

5. Where the files are

The configuration, the data-protection keys and the token certificates live outside the program directory, so that replacing the program never touches them. On Linux /etc/veriqa is root:veriqa 0750 and /var/lib/veriqa is 0700 owned by veriqa: they hold secrets, and only the service account reads them. Why the keys directory is not optional. Data-protection keys protect the sign-in cookies and every value the host encrypts. With no keys directory and no Redis the host keeps them in memory, and every restart invalidates what the previous process issued — users are thrown out of sign-ins that were in flight. The installer therefore sets Veriqa:DataProtection:KeysDirectory in the environment of the service itself, and the directory survives an update and an ordinary uninstall. The service is registered with four environment variables — the environment is where they belong, because they say where the configuration is read from:

The configuration directory

VERIQA_CONFIG_DIRECTORY names a directory, not a file. From it the host reads appsettings.json and appsettings.Production.json, both optional, and layers them over the files shipped with the program. Precedence, weakest to strongest:
  1. appsettings.json of the program directory;
  2. appsettings.Production.json of the program directory;
  3. appsettings.json of the configuration directory;
  4. appsettings.Production.json of the configuration directory;
  5. environment variables (Veriqa__OpenIddict__…);
  6. command-line arguments.
That is why the one line the installer writes — Provider = InMemory — wins over the Provider = PostgreSQL that ships in the program directory, and why anything you pass through the environment still wins over both. If the variable names a directory that does not exist, the host refuses to start and says so: VERIQA_CONFIG_DIRECTORY points to '/etc/veriqa', which does not exist. A directory that exists but holds no files is not an error.
The self-hosted quickstart keeps the integrator’s own settings in config/veriqa.json, resolved against the content root. For a service the content root is the program directory, which an update replaces — so in this delivery mode put your settings into the configuration directory instead.

6. Move to production

The installation ships a template next to the program: appsettings.Production.sample.json. It carries the same keys as the self-hosted quickstart, with REPLACE_ME placeholders for the values. Use it as a reference and edit the configuration file the installer wrote, rather than overwriting that file with it: the template carries no paths to the certificates generated on this machine, and every REPLACE_ME in it is a value you still have to supply.
Keep that copy outside /etc/veriqa: from the edit below on it holds secrets — a bot token, connection strings, certificate passwords — and full removal neither takes it away on Linux nor spares it on Windows, where %ProgramData%\Veriqa goes whole. What to write there is documented once, for both delivery modes:
Replacing the token certificates invalidates every token signed with the old pair, and switching the store from InMemory to a database starts from an empty one: the clients registered in memory have to be declared in the configuration. Do both before the instance carries real sign-ins.

SQL Server on Windows

The Windows archive does not include Microsoft.Data.SqlClient.SNI.dll, the native network library the SQL Server driver uses on Windows: it is Microsoft’s, under the Microsoft Software License Terms, and Veriqa does not redistribute it. PostgreSQL needs nothing, and neither does Linux. With SqlServer selected for any store and the library missing, the service refuses to start, and the message names the Microsoft.Data.SqlClient version and links its nuget.org page. Take the library from the NuGet package Microsoft.Data.SqlClient.SNI.runtime, in the version listed under Dependencies on that page, and put it next to the program. Downloading it, you accept Microsoft’s terms for it:
The installer removes only the files it put in the program directory, so the library stays there through an update; after one, check that the version the new release’s driver lists is still the one you copied. On full removal delete it by hand.

7. HTTPS

The host speaks HTTP on the addresses of --urls / -Urls, and OpenIddict serves /connect/token and the rest of the protocol only over https — so this step is not optional, whether or not the instance is reachable from outside. There are two ways to put TLS in front of it. (a) A reverse proxy on the same machine. Leave --urls at http://127.0.0.1:8080, terminate TLS in NGINX / Caddy / IIS and forward to loopback. For OpenIddict to build https URLs the proxy must forward X-Forwarded-Proto and X-Forwarded-For, and the proxy must be trusted — the same ForwardedHeaders section as in self-hosted, step 6, in the configuration file of the installation:
appsettings.Production.json
Only loopback is trusted by default, so a proxy on the same machine is covered even without this section — writing it out keeps the trust boundary visible in the configuration. A proxy on another host is not covered: put its address here instead, or its network in KnownIPNetworks. The proxy is also where per-IP rate limiting belongs — see Production hardening. (b) A certificate in Kestrel, with no proxy at all. This is plain ASP.NET Core configuration in the same file (Kestrel endpoints):
appsettings.Production.json
The Kestrel:Endpoints section replaces the addresses of ASPNETCORE_URLS rather than adding to them, so no reinstall is needed — but the HTTP endpoint the installer set through --urls is gone with it. Declare an Http endpoint alongside if you still want one (for local health probes, for instance). Keep the server certificate readable by the service account only, next to the token certificates.

8. Verify

Discovery must come back with https URLs; if it comes back with http, the proxy is not trusted — step 7. The full checklist of a deployed instance, including the first sign-in, is self-hosted, step 8. On Linux the service log is in the journal:
On Windows a failed start is recorded by the Service Control Manager in Event Viewer → Windows Logs. To see the message of the host itself, run the executable in a console from the program directory, with the same environment:

Updating

An update replaces the program and keeps the configuration, the data-protection keys and the token certificates: they live outside the program directory on purpose.
On Windows the same two steps: .\install.ps1 -Uninstall from the unpacked archive of the installed version, then .\install.ps1 from the new archive, then Start-Service Veriqa. The second run does not overwrite an existing appsettings.Production.json and does not regenerate existing certificates — it reports that it kept them. If you installed with non-default --service-name, --install-directory or --config-directory, pass the same values to both runs. --urls / -Urls has to be repeated on the installing run as well, and for a different reason: it is not kept anywhere to be read back. The installer writes the unit file — on Windows, the environment of the service — anew every time, so an install without it puts the service back on the default http://127.0.0.1:8080 and the instance stops answering anything but loopback.

Removing

Ordinary removal takes away the service and the program and keeps the configuration, the data-protection keys and the certificates, so that reinstalling over them keeps current sign-ins and secrets:
Run it from the unpacked archive of the installed version: the installer reads that archive to know which files under the program directory are its own, and takes away those. The program directory goes with them once it is empty; if anything else is left in it, the directory stays, and the installer says so instead of clearing it out. Without the archive next to it the installer removes the service and warns that the program files could not be told apart — it does not guess. Full removal also deletes the configuration the installer wrote, the data directory and (on Linux) the veriqa user:
Full removal makes every issued cookie invalid — the data-protection keys are gone with the directory — and the tokens signed with the removed certificates stop being accepted. Copy /etc/veriqa/appsettings.Production.json (or %ProgramData%\Veriqa\appsettings.Production.json) somewhere safe first if this installation is meant to come back. Somewhere safe means outside those directories: a copy left next to the configuration is not a backup — on Windows it goes with %ProgramData%\Veriqa, and on Linux it stays behind, secrets and all, because the directory survives and the installer does not name what is left in it.
On Linux /var/lib/veriqa goes whole, while /etc/veriqa loses the appsettings.Production.json the installer wrote and is removed only when nothing else is left in it. On Windows all three live in %ProgramData%\Veriqa, which goes whole. --remove-data / -RemoveData without the uninstall switch is refused, and nothing is changed. Removing twice is safe: with no service left, the installer says so and takes away what is left of the installation.

Common failures

Failures that stop the host at startup — a missing certificate, an incomplete client, a channel without a token, an unknown provider value — are the same in both delivery modes and are listed in self-hosted, step 9. On Linux the message is in journalctl -u veriqa.

Building the archives yourself

For the release archives the repository carries a pair of scripts that behave identically — build/publish-host.sh and build/publish-host.ps1:
They publish the host self-contained and single-file for linux-x64, linux-arm64 and win-x64, pack each result together with the installer files of its OS, and leave the archives and veriqa-authserver-<version>-SHA256SUMS.txt in artifacts/host/. The version comes from build/Versions.props. What comes out is what this page installs — the same layout, the same appsettings.Production.sample.json, and no appsettings.Development.json.

Next steps

Self-hosted quickstart

Stores, certificates, clients and channels — the configuration itself.

Channel setup

Telegram, WhatsApp, other channels and Email — end to end.

Configuration reference

Every section, key and default in one place.

Production hardening

What to check before shipping.