Use your phone's fingerprint sensor to approve sensitive operations before they execute. A Claude Code hook calls termux-fingerprint, checks the result, and blocks the action if authentication fails.
The fingerprint gate adds a biometric confirmation step before destructive or high-impact operations:
- Pushing to public repositories
- Creating pull requests
- Force-pushing, resetting, or deleting branches
- Any other operation you define as sensitive
If the fingerprint check fails or is dismissed, the operation is blocked. Nothing runs until the device owner physically confirms it.
-
Termux:API package -- install in Termux:
pkg install termux-api
-
Termux:API companion app -- install from F-Droid (search "Termux:API"). The package provides the CLI commands; the companion app provides the Android permissions bridge. Both are required. Without the companion app,
termux-fingerprintfails silently. -
Source matching -- Termux and Termux:API must come from the same source (both F-Droid or both GitHub releases). Mixing sources causes signature mismatches and silent failures.
-
A registered fingerprint on the device (Settings > Security > Fingerprint).
Verify it works before wiring it into hooks:
termux-fingerprintTouch the sensor. You should see JSON output containing
"auth_result": "AUTH_RESULT_SUCCESS".
termux-fingerprint prompts for biometric authentication via the Android system dialog. It returns JSON:
{
"auth_result": "AUTH_RESULT_SUCCESS"
}On failure (wrong finger, dismissed, timeout):
{
"auth_result": "AUTH_RESULT_FAILURE",
"errors": "..."
}A helper function parses auth_result and returns exit code 0 (approved) or 1 (denied). Your hook script sources this function and calls it before allowing the sensitive operation to proceed.
The gate script is also available as examples/fingerprint-gate.sh in this repository.
Create a file that any hook can source. Put it wherever makes sense for your setup -- ~/.claude/hooks/ is a natural choice if you keep your hooks there.
~/.claude/hooks/fingerprint-gate.sh
#!/usr/bin/env bash
# Fingerprint biometric gate -- source this file, then call require_fingerprint
require_fingerprint() {
if ! command -v termux-fingerprint >/dev/null 2>&1; then
echo "termux-fingerprint not found. Install: pkg install termux-api" >&2
echo "Also install the Termux:API companion app from F-Droid." >&2
return 1
fi
local result
result=$(termux-fingerprint)
local auth_result
auth_result=$(echo "$result" | jq -r '.auth_result // ""')
if [ "$auth_result" = "AUTH_RESULT_SUCCESS" ]; then
echo "Fingerprint verified." >&2
return 0
else
echo "Fingerprint denied. Operation blocked." >&2
return 1
fi
}Dependency: This uses
jqto parse JSON. Install it withpkg install jqif you don't have it.
Create a hook that sources the gate and checks for sensitive operations. This example blocks git push to public repos and destructive git commands:
~/.claude/hooks/pre-tool-use-git-safety.sh
#!/usr/bin/env bash
# Block sensitive git operations without fingerprint approval
# Source the gate function
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/fingerprint-gate.sh"
# Read the tool name and input from Claude Code's hook interface
TOOL_NAME="$CLAUDE_TOOL_NAME"
INPUT="$CLAUDE_TOOL_INPUT"
# Only gate Bash tool calls
if [ "$TOOL_NAME" != "Bash" ]; then
exit 0
fi
# Check for sensitive git operations
case "$INPUT" in
*"git push"*|*"git push --force"*|*"git reset --hard"*|*"git branch -D"*|*"gh pr create"*)
echo "Sensitive operation detected: requesting fingerprint approval..." >&2
if require_fingerprint; then
exit 0 # Approved
else
echo '{"decision": "block", "reason": "Fingerprint authentication failed. Operation blocked."}'
exit 0
fi
;;
*)
exit 0 # Not a sensitive operation, allow it
;;
esacMake it executable:
chmod +x ~/.claude/hooks/fingerprint-gate.sh
chmod +x ~/.claude/hooks/pre-tool-use-git-safety.shAdd the hook to your Claude Code settings so it runs before tool use:
~/.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hook": "~/.claude/hooks/pre-tool-use-git-safety.sh"
}
]
}
}The matcher field controls which tool triggers the hook. Setting it to "Bash" means the hook only fires for shell commands. You can also use "*" to fire on every tool call, or name a specific tool like "Write".
Modify the case statement in the hook script to match whatever commands you consider sensitive. Some ideas:
*"rm -rf"*) # Recursive deletion
*"git rebase"*) # History rewriting
*"npm publish"*) # Package publishing
*"gh release"*) # GitHub releasesCheck the remote URL before requiring fingerprint:
REMOTE_URL=$(git remote get-url origin 2>/dev/null || echo "")
case "$REMOTE_URL" in
*"github.com"*)
# Public repo -- require fingerprint
require_fingerprint || exit 1
;;
*)
# Private or no remote -- allow
;;
esacYou can register multiple hooks for the same event. Claude Code runs them in order -- if any hook blocks, the operation stops:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hook": "~/.claude/hooks/pre-tool-use-git-safety.sh"
},
{
"matcher": "Write",
"hook": "~/.claude/hooks/pre-tool-use-file-safety.sh"
}
]
}
}termux-fingerprint: command not found
pkg install termux-apiThen install the Termux:API companion app from F-Droid.
Fingerprint prompt never appears The Termux:API companion app is missing or was installed from a different source than Termux. Uninstall both, reinstall both from F-Droid.
jq: command not found
pkg install jqHook doesn't fire
Check that settings.json is valid JSON and the hook path is correct. Test the hook directly:
CLAUDE_TOOL_NAME=Bash CLAUDE_TOOL_INPUT="git push origin main" bash ~/.claude/hooks/pre-tool-use-git-safety.shFingerprint works in terminal but not in hooks
Hooks run in a subprocess. Ensure the termux-fingerprint binary is on PATH in that context. Try using the full path: /data/data/com.termux/files/usr/bin/termux-fingerprint.
- The fingerprint check runs locally on your device. No biometric data leaves the phone.
termux-fingerprintuses Android's BiometricPrompt API, the same system used by banking apps and password managers.- The gate is only as strong as your hook configuration. If a command can bypass the hook (e.g., by not matching the
casepattern), it won't be gated. - This is a speed bump for autonomous AI operations, not a security boundary. A determined attacker with device access has other vectors. Its purpose is ensuring the device owner approves consequential actions before they happen.