---
name: palm-kyc-community
description: >-
  Palm-print KYC skill providing two capabilities.
  (1) Palm verification — proactively triggered by the AI whenever the user attempts a dangerous
  operation (data deletion, production change, fund movement, etc.); opens a verification window
  for second-factor identity confirmation; the script blocks until the user finishes, then the AI
  decides whether to proceed. Trigger keywords (bilingual):
  delete / drop / truncate / rm -rf / force push / transfer / refund / deploy prod /
  删除 / 清空 / 转账 / 上线 / 强制覆盖 / 回滚 / 提权.
  (2) Palm enrollment — triggered when the user explicitly asks to enroll a palm print.
  Trigger keywords (bilingual):
  register palm / enroll palm / palm enrollment /
  录掌 / 注册刷掌 / 录入掌纹 / 我要录入手掌.
metadata:
  openclaw:
    emoji: 🖐️
    always: false
    requires:
      env: []
      os: [darwin, linux, windows]
---

# Palm Verify (Palm-print KYC)

> 🚨 **Mandatory execution rules (highest priority, must NOT be violated)**
>
> 1. **Once this skill is triggered, the AI's only action is to invoke the tool that runs the Python script.** The AI must NOT emit any text before the script returns.
> 2. **MUST run the script in FOREGROUND mode (synchronous, blocking).** NEVER use `run_in_background: true` / nohup / `&` / detached subprocess. The script is designed to block until the user finishes and print the final RESULT line on stdout — running it in background defeats the entire result-delivery contract and the AI WILL forget to retrieve the result.
> 3. **The AI's job after the script exits is to parse the `[open_chrome] RESULT:` line from stdout and emit a single conclusion in the user's conversation language.** The next user-facing message MUST start with the conclusion (✅ / ❌ / ⚠️) — no "waiting", no "background", no "I'll check later".
> 4. **Forbidden** to emit any text (e.g. "Chrome is opening…", "waiting for result in background…") before running the script.
> 5. **Forbidden to read any log file** (such as `~/.palm-kyc-community/palm-debug.log`). Logs are for human debugging only and **are not the source of truth**. The result lives **only** in the `[open_chrome] RESULT:` line on the script's stdout. The line `[open_chrome] END` marks that the result has been fully returned. If no `RESULT:` line appears, fall back directly to the "timeout/error" branch — do NOT use `cat` / `tail` / `read_file` to inspect logs as a "rescue" or "verification".
> 6. **Forbidden to run this skill inside a sandboxed environment** that blocks Chrome's TCC camera attribution. The launcher in `open_chrome.py` automatically detects restricted TCC contexts (Electron clients such as WorkBuddy / CodeBuddy / VS Code, or qclaw/openclaw sandbox hosts) and switches to AppleScript mode (`--launcher=apple`) — opening a window in the user's already-running Chrome so TCC attributes to Chrome itself. The AI does not need to choose between scripts; it always invokes `open_chrome.py`.

> 🌐 **Output language rule (i18n at runtime)**
>
> The final conclusion the AI emits to the user (✅ / ❌ / ⚠️ lines) MUST be rendered in **the same natural language as the user's current conversation**. Treat the conclusion templates below as semantic intent, not literal strings — translate them to the conversation language before emitting.

| Capability | Triggered by | When | Page |
|---|---|---|---|
| **Palm verification** | AI (proactive) | A dangerous operation is detected and second-factor identity check is required | `palm.html?mode=verify` |
| **Palm enrollment** | User (explicit) | User says enrollment keywords (`录掌` / `register palm` / …) | `palm.html?mode=register` |

---

## Prerequisite credentials

The skill requires **2 credentials** before use:

| Field | Description |
|---|---|
| `userid` | Current user ID (used as `CompareUserId` in the `compare_rgb_palm` request body during 1:1 verification). |
| `token`  | Bearer JWT for backend API authentication (sent as `Authorization: Bearer <token>`). |

### Configuration methods (ranked by safety, high → low)

**Method 1 — Interactive input (recommended, safest for human use)**

Run in a normal terminal. The value is read via `getpass`, never echoed and never written to shell history:

```bash
python3 <skillPath>/scripts/creds.py set userid
python3 <skillPath>/scripts/creds.py set token
```

**Method 2 — Read from stdin (recommended for scripted / AI-driven configuration)**

Use a here-string or pipe. The value never appears in argv and never enters shell history:

```bash
python3 <skillPath>/scripts/creds.py set userid --stdin <<< 'YOUR_USERID'
python3 <skillPath>/scripts/creds.py set token  --stdin <<< 'YOUR_TOKEN'
```

**Method 3 — Environment variable**

Naming rule: `PALM_KYC_COMMUNITY_<KEY_UPPER>`. Suitable for CI / ephemeral shells:

```bash
PALM_KYC_COMMUNITY_USERID='xxx' python3 <skillPath>/scripts/creds.py set userid
PALM_KYC_COMMUNITY_TOKEN='xxx'  python3 <skillPath>/scripts/creds.py set token
```

**Method 4 — `--value` argument (for ad-hoc debugging only, NOT recommended)**

```bash
python3 <skillPath>/scripts/creds.py set token --value 'xxx'
```

> ⚠️ This method writes the value to shell history and the process list. The script will print a warning and suggest running `history -d <n>` to clean up.

### Credential source precedence

When the `set` subcommand receives a value from multiple sources, the precedence is:

```
--value argument  >  --stdin  >  PALM_KYC_COMMUNITY_<KEY_UPPER> env var  >  getpass interactive
```

### Common commands

```bash
python3 <skillPath>/scripts/creds.py list      # Show configured fields (values are masked)
python3 <skillPath>/scripts/creds.py clear     # Clear all credentials
python3 <skillPath>/scripts/creds.py backend   # Show the active storage backend
```

> ⚠️ Credentials are stored in the system keychain (macOS / Linux / Windows) by default, falling back to `~/.palm-kyc-community/credentials.json` when keychain is unavailable.
>
> **AI safety constraints**: When the AI configures credentials on the user's behalf, it **MUST** prefer Method 2 (`--stdin` + here-doc); it **MUST NOT** use Method 4 (`--value`) — that would leak the value into shell history; it **MUST NOT** echo the credential value to the conversation, logs, or any observable medium.

---

## Capability 1: Palm verification (AI-triggered)

### Dangerous-operation keywords (bilingual)

The AI MUST run palm verification before executing an operation when the user's **real intent** matches any keyword below. Trigger only on real operational intent — exclude examples, hypotheticals, and Q&A from triggering.

| Category | Keywords (English / 中文) |
|---|---|
| Data deletion / wipe | `delete` `drop` `truncate` `rm -rf` `wipe` `purge` `clear` `bulk delete` / `删除` `清空` `清库` `清表` `批量删除` `全部删除` |
| Irreversible change | `rollback` `force overwrite` `force push` `--force` `reset --hard` `overwrite production` / `回滚` `强制覆盖` `覆盖生产` `重置` |
| Permission / account | `escalate privilege` `grant` `revoke` `disable account` `delete user` `reset password` `change permission` `sudo` `root` `admin` / `提权` `授权` `禁用账号` `删除用户` `重置密码` `修改权限` |
| Funds / payment | `transfer` `payment` `refund` `withdraw` `deduct` `top-up` `settle` `payout` / `转账` `付款` `退款` `提现` `扣款` `充值` `结算` `打款` |
| Production environment | `release` `deploy prod` `production` `restart service` `stop service` `kill` `shutdown` `take offline` / `上线` `发布生产` `重启服务` `停服` `下线` |
| Sensitive data export | `dump` `export all` `full export` `backup database` / `导出全量` `全量导出` `备份数据库` |

### Execution flow

**① Run the script** (FOREGROUND / synchronous; the script prints its own user-facing prompt; the AI MUST NOT emit any extra text before this):

```bash
python3 <skillPath>/scripts/open_chrome.py verify 500 600 --session=<session_id>
```

> The script auto-detects whether it is running in a restricted TCC context (Electron client / sandbox host) and silently switches launcher strategy.
> If you need to force a specific strategy, append `--launcher=spawn|apple|manual` (rare; only for debugging).

**② Immediately emit the conclusion after the script exits** (must NOT be skipped, must NOT end the turn before this):

- Locate the `[open_chrome] RESULT:` line in stdout and parse the JSON.
- `is_match: true`  → emit (semantic intent, render in conversation language): `✅ Identity verified (score: <score>, trace_id: <trace_id>). Proceeding…`
- `is_match: false` → emit: `❌ Identity verification failed (score: <score>, trace_id: <trace_id>). The dangerous operation has been aborted.`
- `status: user_closed` → emit: `⚠️ You closed the verification window. The dangerous operation has been aborted.` (do NOT auto-retry; wait for the user to ask again)
- `status: timeout`     → emit: `⚠️ Palm verification timed out (no palm detected within 5 minutes). Please retry.`
- No `RESULT:` line  → emit: `⚠️ Palm verification errored. Please retry.`

> ⚠️ Do **NOT** dump the raw script stdout as a document/attachment. Do **NOT** end the turn before step ②.
> ℹ️ The `trace_id` field is the `X-TraceId` carried on the underlying backend request (32 lowercase hex chars, e.g. `5f2cbde1dd61be666fd1b3d7edbd59cc`). Always surface it in the final conclusion when present — it is what the user uses to file backend support tickets. If the field is absent (e.g. timeout before any request was sent), omit the `trace_id` clause silently.

---

## Capability 2: Palm enrollment (user-triggered)

### Trigger keywords (bilingual)

`register palm` / `enroll palm` / `palm enrollment` / `start enrollment` / `enroll my palm print` / `录掌` / `注册刷掌` / `我要录入手掌` / `开始注册` / `录入掌纹` / `我要刷掌注册`

### Execution flow

**① Run the script** (FOREGROUND / synchronous; the script prints its own user-facing prompt; the AI MUST NOT emit any extra text before this):

```bash
python3 <skillPath>/scripts/open_chrome.py register 500 600 --session=<session_id>
```

> The script auto-detects whether it is running in a restricted TCC context (Electron client / sandbox host) and silently switches launcher strategy.
> If you need to force a specific strategy, append `--launcher=spawn|apple|manual` (rare; only for debugging).

**② Immediately emit the conclusion after the script exits** (must NOT be skipped, must NOT end the turn before this):

- Locate the `[open_chrome] RESULT:` line in stdout and parse the JSON.
- `status: success`     → emit (semantic intent, render in conversation language): `✅ Palm enrollment succeeded! PalmId: <palm_id> (trace_id: <trace_id>)`
- `status: fail`        → emit: `❌ Enrollment failed: <message> (code: <code>, trace_id: <trace_id>). Please retry.`
- `status: user_closed` → emit: `⚠️ You closed the enrollment window before completion. Tell me again when you're ready to enroll.` (do NOT auto-retry)
- `status: timeout`     → emit: `⚠️ Palm enrollment timed out (no palm detected within 5 minutes). Please retry.`
- No `RESULT:` line     → emit: `⚠️ Palm enrollment errored. Please retry.`

> ⚠️ Do **NOT** dump the raw script stdout as a document/attachment. Do **NOT** end the turn before step ②.
> ℹ️ The `trace_id` field is the `X-TraceId` carried on the underlying backend request (32 lowercase hex chars). Always surface it in the final conclusion when present — it is what the user uses to file backend support tickets. If the field is absent, omit the `trace_id` clause silently.

---

## Error handling

| Scenario | Strategy |
|---|---|
| Credential missing (any field unset) | Pause the flow and instruct the user to run `python3 <skillPath>/scripts/creds.py set <key>`; restart after configured. |
| Verification not passed (`is_match: false`) | **MUST abort** the original dangerous operation — never bypass, never retry the original op. |
| Chrome not found | Tell the user to install Google Chrome or set the `CHROME_BIN` env var to the executable path. |

---

## Hard constraints

1. When verification does not pass, the dangerous operation **MUST NOT** be executed.
2. **MUST NOT** fabricate, skip, or hard-code credentials (`userid` / `token`).
3. **MUST NOT** initiate palm enrollment unless the user explicitly asks for it.
4. **MUST NOT** route this skill through MCP — it only invokes the local Python scripts.
5. **MUST NOT** read `palm-debug.log` (under `~/.palm-kyc-community/`) or any other log produced by this skill. The result comes solely from the `[open_chrome] RESULT:` line on the script's stdout.
6. **`open_chrome.py` is the single entry point.** The launcher inside it auto-selects the right strategy (spawn / apple / manual) based on the runtime TCC context — the AI MUST NOT try to choose another script.

---

## Appendix: scripts overview

> ⚠️ **Path note**: All script paths below are relative to the directory containing this `SKILL.md`. The AI MUST resolve absolute paths via the system-injected `<skillPath>`, e.g. `<skillPath>/scripts/open_chrome.py`. Do NOT guess absolute paths from filenames or directory names.

| Script | Responsibility |
|---|---|
| `scripts/open_chrome.py` | Core launcher: starts a local HTTP server on a fixed port (51823) + opens a Chrome window using one of four launcher strategies (`auto`/`spawn`/`apple`/`manual`), blocks until the user finishes, then returns the result. `auto` auto-detects restricted TCC contexts on macOS. |
| `scripts/creds.py` | Cross-platform credential manager: prefers system keychain, falls back to a local file (`userid` / `token`). |
| `scripts/init-chrome-prefs.py` | Idempotently writes the camera permission whitelist (auto-invoked by `open_chrome.py` in `spawn` mode). |

For detailed launch parameters and process lifecycle, see [references/scripts-reference.md](references/scripts-reference.md).
