LabTether's security architecture covering transport encryption, authentication, authorization, and hardening recommendations for self-hosted deployments.
| Mode |
Env Value |
Behavior |
| Auto (default) |
LABTETHER_TLS_MODE=auto |
Hub generates ECDSA P-256 CA (10yr) and server cert (1yr, 30-day auto-renewal). HTTPS on :8443, HTTP-to-HTTPS redirect on :8080. |
| External |
LABTETHER_TLS_MODE=external |
User-provided certs via LABTETHER_TLS_CERT/LABTETHER_TLS_KEY (e.g. tailscale cert, Let's Encrypt). Uploaded certs from Settings -> TLS Management also use this path. |
| Disabled |
LABTETHER_TLS_MODE=disabled |
Plain HTTP on :8080. Only appropriate when Tailscale provides WireGuard encryption at the network layer. |
| Component |
Trust Mechanism |
| Agents (public cert) |
System CA pool via https://<host>.<tailnet>.ts.net or other publicly-trusted paths |
| Agents (built-in TLS) |
Trust-On-First-Use (TOFU) via enrollment; agent validates IsCA=true before saving |
| Docker Compose services |
Public CA cert shared via read-only labtether-ca volume; CA private key isolated in labtether-data |
| Web console (Node.js) |
NODE_EXTRA_CA_CERTS pointed at hub CA |
| Browsers |
Manual trust or system-wide CA install; download from GET /api/v1/tls/ca.crt |
| Uploaded hub certs |
Private key encrypted before storage; certificate chain remains readable |
Hub TLS source precedence: UI override > deployment env/config > code defaults
All HTTPS responses include:
| Header |
Value |
Strict-Transport-Security |
max-age=63072000 |
X-Content-Type-Options |
nosniff |
X-Frame-Options |
DENY |
Role-based auth with four built-in roles (owner, admin, operator, viewer). See Permissions for the full RBAC model.
| Property |
Detail |
| Standard |
RFC 6238 TOTP |
| Secret storage |
AES-256-GCM encrypted at rest using the hub encryption key |
| Setup verification |
Valid TOTP code required before activation; unverified secrets discarded |
| Challenge tokens |
In-memory, 5-minute TTL, max 5 failed attempts, single-use on success |
| Recovery codes |
8 single-use codes, bcrypt-hashed at rest, each invalidated after use |
| Disable 2FA |
Requires valid TOTP code or unused recovery code |
| Property |
Detail |
| Format |
lt_<prefix>_<secret> with 32 bytes of cryptographic randomness |
| Storage |
SHA-256 hash only -- raw key never persisted |
| Transport |
TLS required -- keys rejected on plain HTTP |
| Rate limiting |
600 req/min per source IP, 3000 req/min global per key |
| Scoping |
Per-key permissions with optional asset allowlists |
| Comparison |
Constant-time hash comparison (timing-attack resistant) |
| Endpoint |
Purpose |
GET /api/v1/tls/ca.crt |
Download CA certificate (PEM) |
GET /api/v1/tls/info |
TLS status (mode, cert fingerprint, expiry) |
POST /api/v1/enroll |
Agent enrollment with TOFU CA distribution |
| Rule |
Behavior |
| DNS resolution check |
Both hostname and resolved addresses are validated |
| Loopback/link-local/private |
Blocked unless explicitly enabled via security.outbound_allow_private |
| Allowlist bypass prevention |
Hostname allowlists do not bypass resolved-address policy checks |
| Surface |
Enforcement |
GET /status/aggregate |
Scoped to requesting actor (unless owner); cache keys include actor scope |
| Desktop recording paths |
Session ownership enforced with admin override; raw file paths not exposed |
| Desktop credentials |
Gated by interactive session_start policy |
| Persistent terminal sessions |
Re-check interactive session_start policy on create and attach |
| First-run bootstrap |
Generated passwords not printed to stderr; only masked hint logged |
| Rule |
Detail |
| Hub URL format |
Origin-only (http[s]://host[:port]): no credentials, path, query, or fragment |
| HTTPS requirement |
Required for all non-loopback hubs |
| HTTP exception |
Loopback only (localhost, 127.0.0.1, ::1) |
| Keychain storage |
Device-local, non-sync (WhenUnlockedThisDeviceOnly, no iCloud sync) |
| 401 handling |
Clears session artifacts, returns to login, requires re-authentication |
| Sign-out cleanup |
Clears token, cookies, URL cache, offline cache/queues, push registration |
| TLS version |
1.2+ required for secure transport |
| Untrusted TLS setting |
Available in shipping builds, disabled by default, and scoped to the exact selected HTTPS hub origin. Installing the hub CA is recommended but optional. |
| Check |
Action |
| TLS enabled |
Confirm LABTETHER_TLS_MODE is auto or external (not disabled) |
| Certificate trust |
For auto mode, install the hub CA system-wide or accept in browser for both :3000 and :8443. Download from https://<hub>:8443/api/v1/tls/ca.crt |
| CA key isolation |
Verify labtether-ca volume is read-only for agent/console containers (CA private key only in labtether-data) |
| Network access |
Restrict access paths to private network or Tailscale |
| Secrets |
Enforce strong secret/token generation and rotation |
| Connector accounts |
Use least-privilege integration accounts |
| Exposed ports |
Audit exposed ports and reverse-proxy rules |
| Auth logs |
Review auth/session logs for anomalies |
| Backups |
Validate backup encryption and restore access controls |
| Patching |
Keep hub and dependencies patched |
| iOS app |
Use https:// hub URL for all non-local environments; use http://localhost only for loopback development; log out in-app when switching hubs |