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.
On this page
Where the config file lives
| Install | Path |
|---|---|
| Windows (MSI, Windows Service) | C:\Program Files\LymeScribe\appsettings.json |
| Apple Silicon (pkg, launchd agent) | /usr/local/bin/lymescribe/appsettings.json |
| Console run | appsettings.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>
"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.
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
| Setting | What it does | Default |
|---|---|---|
Kestrel:Port | The HTTP port. The server listens on all interfaces so LAN clients can reach it. | 58994 |
SetupComplete | Until 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 |
AllowedHosts | Host-header filtering. * allows all. | * |
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.
| Setting | What it does | Default |
|---|---|---|
WhisperX:ExecutionMode | auto picks WSL on Windows and a direct shell on macOS/Linux. wsl or direct force it. | auto |
WhisperX:WslDistribution | Which WSL distribution to use. Empty means the default one. | "" |
WhisperX:VenvPath | Path to the Python virtual environment holding WhisperX. | ~/whisper-env |
WhisperX:WorkingDirectory | Scratch directory for transcription temp files. | /tmp/LymeScribe |
WhisperX:Model | tiny, base, small, medium, large-v2, large-v3. Smaller is faster and less accurate. | large-v2 |
WhisperX:Language | Transcription language. | en |
WhisperX:InitialPrompt | Optional text to bias the model — useful for house vocabulary and proper nouns. | "" |
WhisperX:Device | cuda for an NVIDIA GPU, or cpu. | cuda |
WhisperX:FallbackDevice | Used if the primary device isn't available. Empty disables the fallback. | cpu |
WhisperX:ComputeType | Quantization: float16 (GPU), int8 (best on CPU), float32. Empty auto-selects. | "" |
WhisperX:CooldownSeconds | How long after the queue empties before the Python environment is shut down. | 45 |
WhisperX:UsePersistentWorker | Keep the worker process alive between jobs instead of restarting it each time. | true |
WhisperX:WorkerWarmUpOnStart | Load the worker at server startup rather than on the first request — trades idle memory for a fast first job. | false |
WhisperX:WorkerIdleTimeoutSeconds | Shut down an idle worker after this long. | 3600 (60 min) |
WhisperX:WorkerStartupTimeoutSeconds | How 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
| Setting | What it does | Default |
|---|---|---|
Storage:TempDirectory | Where uploads land before processing. Created automatically; empty means the system temp directory. | "" |
Storage:MaxUploadSizeMb | Largest 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.
| Setting | What it does | Default |
|---|---|---|
History:Enabled | Master switch for the server-side archive. | true |
History:ArchiveDirectory | Where archived transcripts and retained audio are written. | "" |
History:RetentionDays | Entries older than this are swept away. | 90 |
History:MaxArchiveSizeMb | Size cap for the archive. 0 means no cap. | 0 |
History:CleanupIntervalHours | How often the retention sweep runs. | 6 |
History:EnforceAudioRetention | Force source audio to be archived regardless of what the client asked for. | false |
History:EnforceTranscriptRetention | Force transcripts to be archived regardless of what the client asked for. | false |
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:
- Keep real values in
appsettings.Production.jsonon the server machine and leave the placeholders in the base file. - 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.
- When you back up the server, remember the backup now contains that token.
- Rotate by generating a new token on HuggingFace, pasting it in, restarting, then revoking the old one.
Next
- Administration — running it day to day.
- Installation — if you haven't installed yet.
- The full setup walkthrough.