# Vestibule configuration — example schema # # This file documents the shape of vestibule.toml. The admin wizard # writes its configuration as JSON to browser.storage.local (see # extension/admin.js); usher-side TOML loading for headless # provisioning is a Phase 3 task. The schema is fixed now so the # implementation has a target. # # In production, this file lives at: # Linux: ~/.config/vestibule/vestibule.toml # Windows: %APPDATA%\Vestibule\vestibule.toml [general] # Product name shown in UI name = "Vestibule" [url_policy] # Navigation policy. The wizard writes this block to # browser.storage.local as the `policy` key (see extension/admin.js); # usher-side TOML loading for headless provisioning is a Phase 3 task. # # "safelist" — block every request whose hostname is not on the # safelist (default). Domain-based: an entry grants # the domain and its subdomains, and nothing else. # Third-party resources (CDNs, SSO, analytics) are # gated too — list every domain the kiosk content # needs. Internal schemes (about:, moz-extension:, # chrome:, resource:) and the home page's domain are # always permitted, so an empty list degrades to a # kiosk that shows about:blank and nothing else. # "open" — no filtering # "blocklist" — substring deny (legacy) # "allowlist" — substring allow (legacy; an empty list allows # everything — prefer safelist) mode = "safelist" # Operator-maintained safelist — one hostname per entry. Pasted URLs # are normalized to their hostname. Subdomains are covered by the # parent entry; ports are ignored. safelist = [] [session] # Idle timeout before session resets (seconds). # Patient walks away → after this many seconds of no input, clear ALL # personal data and return to home URL. Independent of device sleep. idle_timeout_s = 300 # What to do on session reset. # "reset" — clear all personal data, navigate to home (default) # "lock" — show unlock overlay, keep current tab visible behind it # "both" — reset then show unlock overlay on_reset = "both" # Home URL — where to navigate after reset. In safelist mode the # wizard refuses to save unless this URL's domain is on the safelist; # the engine additionally exempts it at runtime, so the kiosk can # never block its own front door. home_url = "about:blank" [power] # How Vestibule handles device sleep/wake. # "aware" — detect wake, reset session on resume (default) # "always-on" — block idle sleep via OS inhibit APIs (Phase 3) mode = "aware" # On wake from sleep, what to do. # "reset" — same as on_reset="reset" # "lock" — same as on_reset="lock" # "both" — same as on_reset="both" (default) # "nothing" — ignore wake (testing only; never use in production) on_wake = "both" [unlock] # Password storage location. # "file" — Argon2id PHC string at 0600, owned by the kiosk user # (the shipped implementation; keyring daemons are not # available on minimal kiosk compositors like cage) # "keyring" — OS keyring (reserved for desktop-session deployments) storage = "file" # Unlock method. # "password" — single password (Argon2id hash in file storage) # "pin" — numeric PIN (Argon2id hash in file storage) # "totp" — TOTP + password (Phase 5+) method = "password" # Argon2id parameters for password hashing. # Defaults are conservative; tune for your threat model. [unlock.argon2] memory_kib = 65536 # 64 MiB iterations = 3 parallelism = 4 # ────────────────────────────────────────────────────────────────────── # PLANNED WORK — NOT IMPLEMENTED IN THIS RELEASE # ────────────────────────────────────────────────────────────────────── # Webcam-based presence detection. When a webcam is detected at startup, # usher uses it to: # # 1. Detect when the current user steps away from the kiosk (no face # visible for N seconds). # 2. Detect when a different person approaches (face embedding doesn't # match the one captured at session start). # # In either case, trigger an immediate session reset (same path as idle # timeout, but faster — no need to wait idle_timeout_s). # # Privacy guarantees: # - Frames are processed in-memory only, never written to disk # - Face embeddings never leave the local process # - No telemetry, no cloud API calls # - Webcam LED (if present) is respected; we do not attempt to capture # frames while the camera is in use by another application # # Implementation: OpenCV + dlib (or MediaPipe) running in usher's power # thread sibling. Spike scope TBD in a future session. # # [presence] # enabled = false # auto-enable if webcam detected # camera_index = 0 # away_threshold_s = 10 # no face for this long → reset # face_match_threshold = 0.6 # cosine similarity; lower = stricter # match_window_s = 30 # capture baseline face over first 30s # on_change = "reset" # "reset" | "lock" | "ignore" # privacy_mode = true # never persist any biometric data # ────────────────────────────────────────────────────────────────────── # ADMIN CONFIGURATION — set via the in-browser wizard # ────────────────────────────────────────────────────────────────────── # The admin wizard is opened via: # - The gear icon in the hidden menu (mouse to top of screen) # - Ctrl+Shift+V keyboard shortcut # # The wizard is gated by a setup password (separate from the kiosk unlock # password). On first run, the wizard forces the operator to set this # password before any configuration can be saved. # # [admin] # # Password hashing algorithm for the admin password: # # PBKDF2-SHA-256, 100k iterations, in-browser via Web Crypto. The # # kiosk unlock password uses Argon2id in usher (file storage, 0600). # algorithm = "PBKDF2-SHA-256" # iterations = 100000 # salt_bytes = 16 # # # Recovery: if the admin password is lost, the only recovery path is # # OS-level — delete the vestibule-profile/storage directory and re-run # # the wizard. A backup recovery code is a Phase 5 task. # recovery = "os-level-reset" # # # The wizard's full config schema is mirrored into browser.storage.local # # under the `adminConfig` key. See extension/admin.js for the canonical # # schema. The TOML below is documentation only — the wizard writes # # JSON, not TOML. Headless TOML provisioning is a Phase 3 task. # # [admin.wizard] # # Steps shown in the wizard, in order. Cannot be reordered. # steps = [ # "auth-gate", # set or enter admin password # "kiosk-identity", # name, home URL, attract URL (home domain # # checked against the safelist live) # "url-policy", # mode + safelist/allowlist/blocklist # "session", # idle timeout, onReset, persistence allowlist # "power", # mode, on_wake # "unlock", # method + kiosk unlock password # "review", # JSON review + save (refuses a safelist # # config whose home domain is unlisted) # ]