localharness custodies seeds and moves real money ($LH, sponsored gas). Read
this before reporting, and read the trust model before assuming a bug: the
security boundary is deliberately the host device, and keys are deliberately
plaintext — see "Key custody and trust model" in
localharness.xyz/llms.txt.
Pre-1.0. Only the latest published minor (0.x) receives security fixes.
Older minors are not patched; upgrade.
Use GitHub private vulnerability reporting on compusophy/localharness (enabled). Do not open a public issue with exploit details. If private reporting is unavailable to you, open a plain issue that says only "security report, need a private contact" — no details — and a maintainer will follow up.
There is no bug bounty program pre-1.0. Reports are triaged best-effort by a solo maintainer; expect an acknowledgment within a few days, not an SLA.
Security-sensitive surfaces (in scope):
- Self-custody keys. Identity seeds/keys are stored as plaintext by design
(
~/.localharness/keys/<name>.localharness.keyon the CLI;.lh_walletin browser OPFS, exempt from at-rest encryption because it IS the key root). "The key file is readable by local processes" is the documented trust model, not a vulnerability. A path that leaks a key off the device — over the wire, into logs, to another origin — is very much in scope. - The credit proxy (
proxy/): personal-sign auth,$LHmetering, and the x402 settle path. Bugs that let a caller stream inference without being metered, or settle without a valid signature, are in scope. - The sponsor relay (
proxy/api/sponsor.ts): the mainnet fee_payer signs server-side behind a selector allowlist, per-address rate window, gas cap, and a float circuit-breaker. Any way to make the relay sign outside those gates — or to spend the sponsor's fee-token float on non-gas value — is the highest-severity class here. - x402 signatures (X402Facet): replay past the one-shot nonce, ecrecover / EIP-1271 confusion, or bypassing the price-lock ceiling.
- The diamond owner key (EIP-173
owner()/diamondCut): anything that lets a non-owner cut facets or reach owner-only selectors. - wasm bundle integrity (
web/): the browser app is a static Vercel site; tampering paths (cache poisoning, CSP bypass, cross-origin postMessage abuse of the?signer=1/?rpc=1protocols) are in scope.
Out of scope:
- The Moderato testnet deployment and its embedded low-budget testnet sponsor key (loss capped at its balance, by design).
- The QA fleet personas (
scripts/test-fleet/) and their throwaway keys. - Local-attacker reads of plaintext key files (the documented custody model).
- Social engineering, physical access, denial of service by paying for it.
Per key class, in the order an incident would demand:
- Sponsor relay key (mainnet fee_payer, proxy env only — never in a build):
generate a new key, move the remaining fee-token float to it, set
LH_SPONSOR_KEYin the proxy's Vercel env, redeploy the proxy (cd proxy && vercel --prod). The old key holds nothing but gas float, so exposure is capped at whatever float it still had. This rotation has been done once already (the bundle-exposed testnet-era key was retired). - Deployer / diamond owner key:
transferOwnership(EIP-173) on the diamond to a fresh key immediately; that key gatesdiamondCutand all owner-only facet methods, so it is the single most valuable key in the system. Then rotate any proxy env or script that referenced it. - User seeds: there is no server-side revocation — the remedy is the NFT-transfer race documented in the trust model (llms.txt): transfer the identity NFT to a secure wallet, which revokes all enrolled device signers. It only works if the owner moves before the thief does.
- Float drain in progress: the relay's circuit-breaker
(
LH_RELAY_MIN_FLOAT_WEI) refuses to sign once the sponsor's fee-token balance drops below the floor, bounding a drain automatically. Response: let it trip (or set the floor high to halt sponsorship outright), rotate the sponsor key as above, then audit the selector allowlist and rate window inproxy/api/sponsor.tsbefore refunding the float.
Disclosure after a fix is coordinated with the reporter; pre-1.0 the changelog entry is the advisory.