Skip to content

local-encrypt: document unattended/headless threat model#852

Merged
theoephraim merged 1 commit into
mainfrom
local-encrypt-threat-model
Jul 6, 2026
Merged

local-encrypt: document unattended/headless threat model#852
theoephraim merged 1 commit into
mainfrom
local-encrypt-threat-model

Conversation

@theoephraim

@theoephraim theoephraim commented Jul 2, 2026

Copy link
Copy Markdown
Member

What & why

Documents the threat model for varlock's local encryption in headless / unattended environments — the posture was real but implicit and undocumented.

This started from a real question (a self-hoster storing a 1Password service account token as "secret-zero" on a headless Ubuntu box, hand-rolling TPM sealing via systemd-creds). Digging in surfaced that we already do exactly this natively: on Linux with a TPM the key seals to the TPM and varlock load decrypts unattended, with zero setup. The gap was never "make it work headless" — it's that the unattended posture happens implicitly (whenever no presence gate is set up) and was never written down.

Note: this PR was rebased after #854 (Windows TPM support) merged. The original code changes here — the varlock encrypt unattended note and the types.ts/index.ts backend-comment fixes — landed via #854, so this branch is now docs-only.

Changes

  • New "Unattended decryption (headless servers & CI)" section in the local-encryption guide:
    • the TPM-sealed secret-zero pattern and that it decrypts with no prompt on a headless host
    • what it protects (at rest) vs. what it doesn't (a process already running as your user), with the explicit parallel to systemd-creds
    • setup --linux-biometrics as the opt-in presence gate for interactive machines
    • that the presence gate is a consent prompt, not an at-rest boundary — on Linux the TPM unseal isn't bound to polkit, so same-uid code can unseal directly regardless of the gate
    • a scope note that this unattended path is Linux/Windows only — macOS Secure Enclave always requires presence, so there is no unattended decrypt on macOS (headless Mac / CI falls back to file-based)

Design decision: no opt-in gate

We considered gating unattended decrypt behind an explicit opt-in (refuse-by-default unless a flag says unattended is intended) and decided against it:

  • It changes no security properties — unattended hardware decrypt protects at rest, not against a live same-uid attacker, with or without a flag. (Reinforced by the finding above: the Linux gate isn't even a boundary against that attacker — it's consent-grade.)
  • The presence gate is already opt-in (Linux requires a root setup --linux-biometrics step). "No gate" is the deliberate default, not a silent downgrade.
  • On a server an opt-in is pure friction — it'd be set 100% of the time, and forgetting it becomes a confusing deploy failure.
  • Binding "unattended" to the key at creation was also rejected: presence-gate availability is a property of the machine over time, not of the key — a key-level flag goes stale.

So the right move was honesty (docs), not a gate.

Follow-up (not in this PR)

Fail-open edge. A machine that had a presence gate and loses it (polkit removed, pkcheck gone) silently falls through to unattended decrypt instead of prompting. Because the Linux gate is consent-grade, the security delta is ~nil — but the lost consent prompt is a real UX surprise. The planned fix is a per-machine "a gate was configured here" marker + a TTY-only warning when it disappears (never on headless/CI, to keep deploys quiet) — deliberately not per-key (staleness trap). This would cover Linux and the now-merged Windows path together, so it's tracked as a unified follow-up rather than bolted onto this docs PR.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

bumpy-frog

The changes in this PR will be included in the next version bump.

patch Patch releases

  • varlock 1.9.0 → 1.9.1

Bump files in this PR

Click here if you want to add another bump file to this PR


This comment is maintained by bumpy.

@pkg-pr-new

pkg-pr-new Bot commented Jul 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/varlock@852

commit: 621a344

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
varlock-website 89e72cb Commit Preview URL

Branch Preview URL
Jul 06 2026, 07:18 PM

@theoephraim theoephraim requested a review from philmillman July 2, 2026 23:01
@theoephraim theoephraim force-pushed the local-encrypt-threat-model branch from 621a344 to 150a701 Compare July 6, 2026 18:47
Adds a threat-model section to the local-encryption guide: TPM-sealed
unattended decryption for headless/CI hosts, what it protects at rest
vs. a process already running as your user, and that the Linux presence
gate is a consent prompt rather than an at-rest boundary (the TPM unseal
isn't bound to polkit).

Backend-comment and encrypt-note code changes from the original version
are dropped here — they landed via #854 (Windows TPM support).
@theoephraim theoephraim force-pushed the local-encrypt-threat-model branch from 150a701 to 89e72cb Compare July 6, 2026 19:15
@theoephraim theoephraim merged commit 8e71021 into main Jul 6, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants