# Module BSV::Wallet::CLI::Dispatcher <a id="module-BSV-Wallet-CLI-Dispatcher"></a>

<code>bin/wallet</code> argv router. The dispatcher's job is the boring parts
(parsing global flags, enforcing the secrets policy, selecting the command
class, translating exceptions to exit codes). All command-specific behaviour
lives in +Commands::<Verb>+ subclasses.

Public entry point: +Dispatcher.call(argv)+ — typically wired to `ARGV` by
<code>bin/wallet</code>.

## Constants
### `COMMANDS` <a id="constant-COMMANDS"></a> <a id="COMMANDS-constant"></a>
Explicit command registry. Frozen hash beats `const_get` gymnastics (no
autoload races) and beats <code>case/when</code> (greppable). Adding a command
means a line here AND a +Commands::<Verb>+ class — no implicit registration.

### `MESSAGE_REDACTION` <a id="constant-MESSAGE_REDACTION"></a> <a id="MESSAGE_REDACTION-constant"></a>
Field-name + separator capture group. Built from the same canonical list as
<code>Secrets::SENSITIVE_FIELD</code> so JSON-field redaction and string-level
redaction stay consistent. Compound identifiers like `sender_identity_key`
pass through unredacted (interchange identifier, not secret material; see the
pubkey-hex carve-out).

## Public Class Methods
### `boot_engine(opts)` <a id="method-c-boot_engine"></a> <a id="boot_engine-class_method"></a>
Boot the engine with the parsed global options. Threads `wif_override` /
`database_url_override` into <code>CLI.boot</code>. <code>CLI.boot</code>
accepts both as `nil`-defaulted kwargs, falling back to ENV/Fixtures when not
supplied — backward-compat for <code>bin/walletd</code> and other callers.

### `call(argv)` <a id="method-c-call"></a> <a id="call-class_method"></a>
Entry point. Returns the process exit code; <code>bin/wallet</code> passes
that to `exit`. Never raises uncaught — every error path resolves to a
structured stderr line + exit code.
- **@param** `argv` [Array<String>]
- **@return** [Integer]

### `check_database_url!(url)` <a id="method-c-check_database_url-21"></a> <a id="check_database_url!-class_method"></a>
<code>--database-url</code> accepts a URL but rejects an embedded password in
`userinfo`. Argv exposure for DB credentials is the same leakage path as WIF.
- **@return** [String, nil]

### `env_key_allowed?(key)` <a id="method-c-env_key_allowed-3F"></a> <a id="env_key_allowed?-class_method"></a>
- **@return** [Boolean]

### `load_env_file!(path, allow_symlink)` <a id="method-c-load_env_file-21"></a> <a id="load_env_file!-class_method"></a>
+--env=<file>+ loader. Strict policy:
    1. +File.lstat+ FIRST to detect symlinks (regular +File.stat+
       silently follows them and would miss the policy).
    2. Mode + owner check on the lstat'd entry.
    3. +File.realpath+ LAST — realpath resolves symlinks, so it
       must run after the symlink check or the policy is defeated.

Only keys with the documented <code>BSV_WALLET_*</code> /
<code>DATABASE_URL_*</code> / <code>PG*</code> prefixes are loaded; arbitrary
ENV injection from an attacker-writable file is refused.
- **@raise** [UsageError]

### `parse_global_options(argv)` <a id="method-c-parse_global_options"></a> <a id="parse_global_options-class_method"></a>
Parse the global flag layer; everything after the first non-flag token is left
for the subcommand. Enforces the secrets-on-the-CLI policy in-line:
    - +--wif=<wif>+ on TTY without +--allow-insecure-wif+ → refuse
    - +--database-url+ with embedded password               → refuse
    - +--wif-file=<path>+ mode/owner check
    - +--env=<file>+ lstat → mode/owner → realpath ordering
- **@param** `argv` [Array<String>]
- **@return** [Array(GlobalOptions, Array<String>, Boolean)] +[opts, remaining, help_requested]+ — the parsed value
object, the remaining argv after global flags are consumed,
and a flag indicating +-h+/+--help+ was set anywhere in
the global slice.

### `print_global_help()` <a id="method-c-print_global_help"></a> <a id="print_global_help-class_method"></a>
Not documented.

### `read_wif_file!(path)` <a id="method-c-read_wif_file-21"></a> <a id="read_wif_file!-class_method"></a>
Read a WIF from +--wif-file=<path>+. Mode-checked (must be 0600 or stricter),
owner-checked, symlink-refused. Same shape as the <code>--env</code> loader:
`lstat` first (symlink detection happens before any read), then mode/owner,
then read. All syscall errors get wrapped as `UsageError` to preserve the
dispatcher's never-raises-uncaught contract.
- **@raise** [UsageError]
- **@return** [String]

### `redact_message(message)` <a id="method-c-redact_message"></a> <a id="redact_message-class_method"></a>
Apply `Secrets` patterns at the string level. Exception messages may quote
argv tokens (a malformed +--wif=<wif>+ value, a <code>--database-url</code>
containing a password) — bubbling those to stderr verbatim would defeat the
secrets policy. Matches the same field names
<code>Secrets::SENSITIVE_FIELD</code> scrubs in JSON, including the carve-outs
for interchange identifiers (`identity_key`, `public_key`) which stay visible.
Token-shaped (+w++) to avoid greedy spans across whitespace.

### `resolve_wif_override(wif_argv, wif_file, allow_insecure_wif)` <a id="method-c-resolve_wif_override"></a> <a id="resolve_wif_override-class_method"></a>
WIF resolution: <code>--wif-file</code> > <code>--wif</code> (if allowed) >
nil. Returns the raw WIF string for downstream consumption, or `nil` if
neither flag was set (falls through to ENV/Fixtures).
- **@return** [String, nil]
