Configuration
Advanced configuration options for Secure LSL.
Configuration File
Secure LSL uses LSL's standard configuration file (lsl_api.cfg) with an added [security] section.
File Locations
The configuration file is searched in this order:
$LSLAPICFGenvironment variable (if set)./lsl_api.cfg(current working directory)~/.lsl_api/lsl_api.cfg(user home directory)/etc/lsl_api/lsl_api.cfg(system-wide, Linux/macOS)%PROGRAMDATA%\lsl_api\lsl_api.cfg(system-wide, Windows)
File Format
[security]
enabled = true
private_key = base64_encoded_ed25519_private_key_here
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 86400
[log]
level = 4
Security Section
enabled
Type: boolean
Default: true (if private_key is present)
Enables or disables security. When disabled, the device operates in legacy insecure mode.
Warning
Setting enabled = false when other devices have security enabled will cause connection failures.
private_key
Type: string (base64-encoded)
Required: Yes, unless encrypted_private_key is set
The Ed25519 private key for this device. Generated automatically by lsl-keygen.
Never Share This
The private key uniquely identifies this device. Never copy it to other devices or share it.
encrypted_private_key
Type: string (base64-encoded)
Required: No (alternative to private_key for two-factor authentication)
The Ed25519 private key encrypted with a passphrase. Generated by lsl-keygen --passphrase.
This enables two-factor authentication:
- Something you have: the key file
- Something you know: the passphrase
NIS2 Compliance
Using passphrase-protected keys satisfies NIS2 multi-factor authentication requirements for environments where MFA is mandated.
Unlocking at runtime:
- Environment variable: Set
LSL_KEY_PASSPHRASEbefore starting your application - Programmatic: Call
lsl_security_unlock(passphrase)in your code
// Option 2: Programmatic unlock (C++)
#include <lsl_security.h>
auto& sec = lsl::security::LSLSecurity::instance();
sec.initialize();
if (sec.is_locked()) {
sec.unlock("your-passphrase");
}
key_created
Type: ISO 8601 timestamp
Default: Set by lsl-keygen
When the key was generated. Used for key rotation tracking.
session_key_lifetime
Type: integer (seconds)
Default: 86400 (24 hours)
How often session keys are rotated. Lower values provide more forward secrecy but slightly more overhead.
| Value | Use Case |
|---|---|
| 3600 | High-security clinical environments |
| 86400 | Standard research use (default) |
| 604800 | Long-running experiments with minimal overhead |
public_key
Type: string (base64-encoded) Required: No (derived from private_key automatically)
The Ed25519 public key. Generated automatically from the private key.
Device-Bound Session Tokens
For passphrase-protected keys, you can create a device-bound session token to avoid entering the passphrase on every startup. The token is cryptographically bound to your specific hardware.
Creating a Session Token
# Create session token (default: 30 days expiry)
./lsl-config --remember-device --passphrase
# Enter your passphrase when prompted
# Create with custom expiry (90 days)
./lsl-config --remember-device --passphrase --days 90
# Create with no expiry (never expires)
./lsl-config --remember-device --passphrase --days -1
How It Works
- Device ID: A unique identifier derived from your hardware (CPU, motherboard, etc.)
- Token file: Stored in
~/.lsl_api/with encrypted session key - Auto-unlock: On startup, if token is valid and matches device, key unlocks automatically
Managing Session Tokens
# Show your device ID
./lsl-config --show-device-id
# Remove session token (will require passphrase again)
./lsl-config --forget-device
Security Properties
- Device-bound: Token only works on the specific hardware where it was created
- Time-limited: Expires after configured duration
- Revocable: Can be removed with
--forget-device - Hardware-tied: Moving the token file to another device won't work
Use Cases
- Lab workstations: Create long-lived tokens for dedicated recording machines
- Portable devices: Use shorter expiry for laptops that may be lost/stolen
- Headless systems: Essential for servers that need unattended startup
Log Section
level
Type: integer (0-6)
Default: 4 (Info)
Controls log verbosity:
| Level | Name | Description |
|---|---|---|
| 0 | Off | No logging |
| 1 | Fatal | Only fatal errors |
| 2 | Error | Errors |
| 3 | Warning | Warnings and errors |
| 4 | Info | General information |
| 5 | Verbose | Detailed information |
| 6 | Debug | Full debug output |
Environment Variables
LSLAPICFG
Override the configuration file location:
Useful for:
- Testing with different configurations
- Running multiple LSL instances with different keys
- Containerized deployments
PYLSL_LIB
Point Python to a specific liblsl library:
Using lsl-keygen
The lsl-keygen tool generates keys and creates the configuration file.
Basic Usage
By default, you will be prompted for a passphrase to protect the private key (like SSH keys).
Creates keys at the default location (~/.lsl_api/lsl_api.cfg).
Options
| Option | Description |
|---|---|
--output PATH |
Write configuration to specific path |
--force |
Overwrite existing configuration |
--insecure |
Store private key WITHOUT passphrase protection (not recommended) |
--show-public |
Display public key and fingerprint after generation |
--export NAME |
Generate and export keys to NAME.pub and NAME.key.enc files |
--export-public |
Display current public key for sharing |
--import FILE |
Import encrypted key file (.key.enc) into local config |
--help |
Show help message |
Examples
# Generate keys with passphrase protection (default)
./lsl-keygen
# Generate to specific file
./lsl-keygen --output /path/to/lsl_api.cfg
# Regenerate keys (overwrites existing)
./lsl-keygen --force
# Generate WITHOUT passphrase (not recommended for production)
./lsl-keygen --insecure
# Export keys for distribution to other devices
./lsl-keygen --export lab_eeg
# Import an exported key on another device
./lsl-keygen --import lab_eeg.key.enc
Passphrase Protection (Default)
By default, lsl-keygen prompts for a passphrase to encrypt the private key:
$ ./lsl-keygen
LSL Security Key Generator
==========================
Private key will be encrypted with a passphrase for security.
(Press Enter for no passphrase, but this is NOT recommended)
Enter passphrase: ********
Confirm passphrase: ********
Generating Ed25519 keypair...
[OK] Keypair generated successfully!
[OK] Configuration saved to: /Users/you/.lsl_api/lsl_api.cfg
[OK] Private key encrypted with passphrase (2FA enabled)
To unlock at runtime (in order of preference):
1. Device-bound session token: lsl-config --remember-device (recommended)
2. Environment variable: LSL_KEY_PASSPHRASE (less secure)
3. Programmatic: call lsl_security_unlock() in your application
This satisfies NIS2 multi-factor authentication requirements by combining:
- Something you have: the encrypted key file
- Something you know: the passphrase
Environment Variable Security
While you can use LSL_KEY_PASSPHRASE for convenience, environment variables
are visible to other processes on the same system. For better security, use
device-bound session tokens instead:
Low-Risk Environments
For closed lab environments without regulatory requirements (EU CRA, NIS2, HIPAA, GDPR), you can skip the passphrase:
- Press Enter twice when prompted for passphrase (requires confirmation)
- Use
--insecureflag to skip the prompt entirely (shows warning)
Using lsl-config
The lsl-config tool validates and displays configuration.
Basic Usage
Output
Security configuration status:
Config file: /Users/you/.lsl_api/lsl_api.cfg
Security enabled: true
Key created: 2025-12-05T19:00:00Z
Key fingerprint: SHA256:70:14:e1:b5:7f:93:ae:af...
[OK] Configuration valid
Options
| Option | Description |
|---|---|
--check |
Validate configuration and show status (default) |
--show-public |
Display public key and fingerprint for sharing |
--show-device-id |
Display this device's unique hardware identifier |
--remember-device |
Create device-bound session token (use with --passphrase) |
--forget-device |
Remove device-bound session token |
--days N |
Set session token expiry in days (use with --remember-device) |
--check-network |
Scan network for LSL streams and verify security |
--help |
Show help message |
Displaying Public Key for Sharing
Use --show-public to display your device's public key:
$ ./lsl-config --show-public
LSL Device Public Key
=====================
Fingerprint:
SHA256:70:14:e1:b5:7f:93:ae:af...
Public Key (base64):
abc123DEF456...
This public key can be safely shared with other lab members.
Example Configurations
Research Lab (Default)
Standard configuration for most research environments:
[security]
enabled = true
private_key = MC4CAQAwBQYDK2VwBCIEIPt8vW9...
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 86400
[log]
level = 4
Clinical Environment
High-security configuration for clinical deployments:
[security]
enabled = true
private_key = MC4CAQAwBQYDK2VwBCIEIPt8vW9...
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 3600 ; Rotate keys hourly
[log]
level = 3 ; Warnings only in production
Development/Testing
Debug configuration for development:
[security]
enabled = true
private_key = MC4CAQAwBQYDK2VwBCIEIPt8vW9...
key_created = 2025-12-05T19:00:00Z
session_key_lifetime = 86400
[log]
level = 6 ; Full debug output
Legacy Mode (Not Recommended)
For backward compatibility with systems that cannot be updated:
Security Risk
Legacy mode provides no encryption or authentication. Use only when absolutely necessary and only on isolated networks.
Multi-Device Setup
Shared Key Authorization Model
Secure LSL uses a shared keypair model for authorization. All devices that need to communicate securely must have the same keypair (public + private key). Devices with different keys will be rejected as "not authorized."
This model ensures:
- Simple deployment: One key pair for your entire lab
- Clear authorization: Only devices with your lab's key can connect
- Easy management: Add new devices by importing the shared key
Setting Up Multiple Devices
Step 1: Generate the lab key on your admin/primary machine:
# Generate with passphrase protection (recommended)
./lsl-keygen --passphrase --force
# Enter a strong passphrase - you'll need this for all devices
# Export for distribution
./lsl-keygen --export lab_shared
# Creates: lab_shared.pub and lab_shared.key.enc
Step 2: Distribute to all lab devices:
# Copy encrypted key to each device
scp lab_shared.key.enc user@device1:~/.lsl_api/
scp lab_shared.key.enc user@device2:~/.lsl_api/
scp lab_shared.key.enc pi@raspberry-pi:/tmp/
Step 3: Import on each device:
# On each device, import the shared key
./lsl-keygen --import /path/to/lab_shared.key.enc
# Enter the same passphrase used during generation
# Verify the fingerprint matches
./lsl-config --show-public
# All devices should show: SHA256:xx:xx:xx:xx...
Step 4: (Optional) Create device-bound session tokens:
# On each device, create a session token for unattended operation
./lsl-config --remember-device --passphrase
# Enter passphrase once, then no more prompts on this device
Verification
All authorized devices should show the same fingerprint:
# On any device
./lsl-config --show-public
# Output should match across all devices:
# Fingerprint: SHA256:79:8c:1d:7d:a6:b4:34:22...
What Happens with Different Keys
If a device has a different key, connections are rejected:
This is intentional security behavior - only devices with your lab's shared key can participate in secure streams.
Security Considerations
- Exported keys are always passphrase-protected (Argon2id + ChaCha20-Poly1305)
- The
.key.encfile is safe to transfer over untrusted networks - Each device needs the passphrase to unlock (or a device-bound session token)
- Treat the passphrase like a lab password - share only with authorized personnel
Next Steps
- Quick Start Guide - Basic usage
- How Encryption Works - Technical details
- Troubleshooting - Common issues