← Server Guide

Configuring LymeScribe Server

Most of what you'll ever need is set by the first-run wizard. This chapter is the reference for the file underneath it — appsettings.json — and for the credentials that don't live in it.

Last updated 2026-08-31

On this page

  1. Where the config file lives
  2. API keys — how clients authenticate
  3. The HuggingFace token
  4. Network & port
  5. Model, device, and the worker
  6. Storage & upload limits
  7. History & retention
  8. TLS, CORS, and rate limiting
  9. Logging
  10. Where secrets belong

Where the config file lives

InstallPath
Windows (MSI, Windows Service)C:\Program Files\LymeScribe\appsettings.json
Apple Silicon (pkg, launchd agent)/usr/local/bin/lymescribe/appsettings.json
Console runappsettings.json next to the binary

All three deployments read the same file format. Restart the server after editing it — the service on Windows, or the launchd agent on macOS.

Anything in appsettings.Production.json (or appsettings.Development.json on a dev box) overrides the base file. That's the conventional place for the values you don't want in a shared or version-controlled copy of the base config.

API keys — how clients authenticate

Every request from a desktop client carries an API key in an HTTP header:

X-API-Key: tlk_<64 hex characters>

Keys are named and individually revocable — issue one per person or per device, so losing a laptop costs you one key rather than a server-wide password change. Each key also has a scope (Full or DictationOnly) and a name, which is what shows up against that client's jobs in server logs and history.

They are stored as SHA-256 hashes in api-keys.json alongside the binaries. Plaintext is never written anywhere — a key is displayed once, at creation, and cannot be recovered afterwards.

Create and manage them from the admin dashboard (http://<server>:58994/admin → Keys), or from the command line in the install directory:

LymeScribe.Api keys create --name "Front Desk MacBook" --scope full
LymeScribe.Api keys list
LymeScribe.Api keys revoke <key-id>
The "ApiKey" field in appsettings.json is a leftover — leave it empty. Authentication is driven entirely by api-keys.json. Putting a value in the config field does not create a working key.
An API key is a credential to the whole server, not to a private slice of it. LymeScribe Server is designed as a single-tenant appliance for one trusted group on a trusted network: any valid key can list and fetch the server's history and see the live queue, and every connected client receives progress and completion events for every job — including other people's transcript text. Issue keys only to people you would let read everything on that server. If you need genuine per-user privacy between groups, run separate servers.

The HuggingFace token

Speaker labeling uses gated models that require a free HuggingFace account: you accept their license once on the website, then generate an access token. Without a valid token the server still transcribes — you just don't get speaker labels.

"WhisperX": {
  "HuggingFaceToken": "hf_YOUR_TOKEN_HERE"
}

The setup wizard writes this for you; you'd only touch it by hand to rotate the token. Full instructions: HuggingFace account & token setup.

Network & port

SettingWhat it doesDefault
Kestrel:PortThe HTTP port. The server listens on all interfaces so LAN clients can reach it.58994
SetupCompleteUntil this is true, every API request except the health check is refused with a "setup is not complete" message. The wizard sets it.set at setup
AllowedHostsHost-header filtering. * allows all.*
Changing the port: use Kestrel:Port. The ASPNETCORE_URLS environment variable has no effect here — the server sets its own listener explicitly and overrides it. The equivalent environment variable, if you prefer one, is Kestrel__Port.

If you change the port on Windows, remember the MSI's firewall rule was opened for 58994 — you'll need a rule for the new port.

Model, device, and the worker

These are all server-wide. Clients cannot request a different model, language, or prompt per job — if you need two different setups, you need two servers.

SettingWhat it doesDefault
WhisperX:ExecutionModeauto picks WSL on Windows and a direct shell on macOS/Linux. wsl or direct force it.auto
WhisperX:WslDistributionWhich WSL distribution to use. Empty means the default one.""
WhisperX:VenvPathPath to the Python virtual environment holding WhisperX.~/whisper-env
WhisperX:WorkingDirectoryScratch directory for transcription temp files./tmp/LymeScribe
WhisperX:Modeltiny, base, small, medium, large-v2, large-v3. Smaller is faster and less accurate.large-v2
WhisperX:LanguageTranscription language.en
WhisperX:InitialPromptOptional text to bias the model — useful for house vocabulary and proper nouns.""
WhisperX:Devicecuda for an NVIDIA GPU, or cpu.cuda
WhisperX:FallbackDeviceUsed if the primary device isn't available. Empty disables the fallback.cpu
WhisperX:ComputeTypeQuantization: float16 (GPU), int8 (best on CPU), float32. Empty auto-selects.""
WhisperX:CooldownSecondsHow long after the queue empties before the Python environment is shut down.45
WhisperX:UsePersistentWorkerKeep the worker process alive between jobs instead of restarting it each time.true
WhisperX:WorkerWarmUpOnStartLoad the worker at server startup rather than on the first request — trades idle memory for a fast first job.false
WhisperX:WorkerIdleTimeoutSecondsShut down an idle worker after this long.3600 (60 min)
WhisperX:WorkerStartupTimeoutSecondsHow long to wait for the worker process to come up.120

CPU-only servers

A machine with no usable NVIDIA GPU should be configured explicitly rather than relying on the fallback:

"Device": "cpu",
"FallbackDevice": "",
"ComputeType": "int8"

int8 quantization roughly doubles CPU inference speed. Expect CPU transcription with large-v2 to be an order of magnitude slower than a GPU; if the wait is painful, dropping to base or small buys back a lot of it at some cost in accuracy.

Storage & upload limits

SettingWhat it doesDefault
Storage:TempDirectoryWhere uploads land before processing. Created automatically; empty means the system temp directory.""
Storage:MaxUploadSizeMbLargest file a client may upload.500

Accepted audio and video extensions: .wav .m4a .mp3 .mp4 .mov .mkv .avi .flac .ogg .wma .webm.

History & retention

The server keeps its own archive of completed jobs, separate from each client's local history. This is what makes a transcript retrievable after a client machine is wiped — and it's also the thing to think hardest about if you handle sensitive recordings.

SettingWhat it doesDefault
History:EnabledMaster switch for the server-side archive.true
History:ArchiveDirectoryWhere archived transcripts and retained audio are written.""
History:RetentionDaysEntries older than this are swept away.90
History:MaxArchiveSizeMbSize cap for the archive. 0 means no cap.0
History:CleanupIntervalHoursHow often the retention sweep runs.6
History:EnforceAudioRetentionForce source audio to be archived regardless of what the client asked for.false
History:EnforceTranscriptRetentionForce transcripts to be archived regardless of what the client asked for.false
The two Enforce… switches are admin policy and they win. Normally each client decides per job whether its audio and transcript are kept server-side. Turning these on overrides that choice — useful when your organisation needs a complete record, and worth telling your users about, because their "don't keep this" setting will be quietly ignored.

TLS, CORS, and rate limiting

TLS is off by default, and plain HTTP over a private LAN is the normal deployment. Turning it on adds an HTTPS listener alongside the HTTP one rather than replacing it:

"Tls": {
  "Enabled": true,
  "Port": 58995
}

With no certificate supplied the server generates its own CA and server certificate. Because that CA is self-signed, clients have to trust it — the desktop apps have a "Use HTTPS" toggle with a Fetch from Server button that downloads and installs the CA certificate for you. To use a certificate you already own, set Tls:CertificatePath and Tls:CertificatePassword instead.

CORS only matters for browser callers. With nothing configured, the default policy allows localhost and the usual private network ranges; set Cors:AllowedOrigins to an explicit list to narrow it.

Rate limiting defaults to RateLimiting:RequestsPerMinute: 60, counted per API key. Exceeding it returns HTTP 429 with a Retry-After header. The health check is exempt.

Logging

"Logging": {
  "MinimumLevel": "Information",
  "RetentionDays": 30,
  "MaxFileSizeMb": 100
}

The server writes a rolling daily log file to a logs/ directory in its install location — lymescribe-YYYY-MM-DD.log, in a compact JSON format. On the Mac install these go to /Users/Shared/LymeScribe/logs/. Raise MinimumLevel to Debug temporarily when you're chasing a problem, and put it back afterwards; debug logging is verbose.

Where secrets belong

Two things in this file are genuinely sensitive: the HuggingFace token and, if you supply your own certificate, the TLS certificate password. API keys and the admin password are not in this file at all — keys live hashed in api-keys.json, and the admin password is set with the CLI:

LymeScribe.Api admin reset-password

Practical rules:

  1. Keep real values in appsettings.Production.json on the server machine and leave the placeholders in the base file.
  2. Never commit a config file containing a real token to source control, including a private repository — rotating a leaked HuggingFace token is easy, but only if you notice.
  3. When you back up the server, remember the backup now contains that token.
  4. Rotate by generating a new token on HuggingFace, pasting it in, restarting, then revoking the old one.

Next