feat(access): Bolt Card tap-to-enter access gate (ADR-003) #86

Merged
padreug merged 11 commits from feat/access-control-skeleton into dev 2026-09-20 13:16:22 +00:00
4 changed files with 8 additions and 7 deletions
Showing only changes of commit 04767080a1 - Show all commits

fix(access): dev unlock defaults OFF

ACCESS_DEV_UNLOCK was opt-out (anything but 'false' enabled it) and
access.example.json shipped it on, so a gated production machine would
render a visible gate-bypass button on the lock screen by default. Flip
to opt-in (=== 'true'), update the example file and the provisioning
schema comment to match.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Padreug 2026-09-20 14:11:38 +02:00

View file

@ -108,9 +108,9 @@ VITE_SPIRE_SEED=
# Turn OFF once a real allow-list (/var/lib/bitspire/access.json) is provisioned. # Turn OFF once a real allow-list (/var/lib/bitspire/access.json) is provisioned.
# ACCESS_OPEN_ENROLLMENT=true # ACCESS_OPEN_ENROLLMENT=true
# Allow the on-screen runtime dev/operator unlock button (default: allowed when # Show the on-screen runtime dev/operator unlock button on the locked screen.
# the gate is on). Set to 'false' to hide it on a locked-down deployment. # Default OFF — it bypasses the gate, so enable only on a bench/dev machine.
# ACCESS_DEV_UNLOCK=false # ACCESS_DEV_UNLOCK=true
# Per-machine salt for hashing credentials/PINs. Provision a real value in # Per-machine salt for hashing credentials/PINs. Provision a real value in
# production (or in access.json); a fixed default is used if unset. # production (or in access.json); a fixed default is used if unset.

View file

@ -181,8 +181,9 @@ function loadAccessControl() {
// deployed machine by dropping a file + restarting the service, with no image // deployed machine by dropping a file + restarting the service, with no image
// rebuild. Defaults OFF. // rebuild. Defaults OFF.
let enabled = process.env.ACCESS_CONTROL_ENABLED === 'true' let enabled = process.env.ACCESS_CONTROL_ENABLED === 'true'
// Dev unlock allowed by default when the gate is on; opt out explicitly. // Dev unlock is OFF unless explicitly enabled: a gated machine must not ship
let devUnlock = process.env.ACCESS_DEV_UNLOCK !== 'false' // a visible bypass button by default.
let devUnlock = process.env.ACCESS_DEV_UNLOCK === 'true'
let openEnrollment = process.env.ACCESS_OPEN_ENROLLMENT === 'true' let openEnrollment = process.env.ACCESS_OPEN_ENROLLMENT === 'true'
let salt = process.env.ACCESS_SALT || '' let salt = process.env.ACCESS_SALT || ''
let allowList: AccessAllowListEntry[] = [] let allowList: AccessAllowListEntry[] = []

View file

@ -1,5 +1,5 @@
{ {
"enabled": true, "enabled": true,
"openEnrollment": true, "openEnrollment": true,
"devUnlock": true "devUnlock": false
} }

View file

@ -13,7 +13,7 @@
# { # {
# "enabled": true, // master switch for the gate # "enabled": true, // master switch for the gate
# "openEnrollment": true, // prototype: admit any valid npub # "openEnrollment": true, // prototype: admit any valid npub
# "devUnlock": true, // allow the on-screen dev/operator unlock # "devUnlock": false, // on-screen dev/operator unlock (bypasses the gate; default off)
# "salt": "per-machine", // hashing salt (provision a real one for prod) # "salt": "per-machine", // hashing salt (provision a real one for prod)
# "allowList": [ // authorized identities (hashed); empty in open mode # "allowList": [ // authorized identities (hashed); empty in open mode
# { "idHash": "<hashId(hexpubkey,salt)>", "role": "user", "pinHash": "<hashPin(pin,salt)>" } # { "idHash": "<hashId(hexpubkey,salt)>", "role": "user", "pinHash": "<hashPin(pin,salt)>" }