Skip to content

Security: compusophy/localharness

SECURITY.md

Security policy

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.

Supported versions

Pre-1.0. Only the latest published minor (0.x) receives security fixes. Older minors are not patched; upgrade.

Reporting a vulnerability

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.

Scope

Security-sensitive surfaces (in scope):

  • Self-custody keys. Identity seeds/keys are stored as plaintext by design (~/.localharness/keys/<name>.localharness.key on the CLI; .lh_wallet in 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, $LH metering, 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=1 protocols) 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.

Incident response runbook

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_KEY in 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 gates diamondCut and 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 in proxy/api/sponsor.ts before refunding the float.

Disclosure after a fix is coordinated with the reporter; pre-1.0 the changelog entry is the advisory.

There aren't any published security advisories