v0.17.0
Device linking is reworked to Option A — identity is the seed, carried
between devices by QR seed-transport (no on-chain pairing, no per-device
keys, no glue). Platform $LH credits become the primary path to model
access (BYOK is the second option), agents pay each other in $LH over
x402, and a batch of registry quality-of-life facets land.
Added
- Multi-device via QR seed-transport (Option A). "Add a device" (apex
admin) encrypts this device's seed under a one-time code and renders a QR
oflocalharness.xyz/?adopt=1#s=<ciphertext>— the encrypted seed rides
the URL fragment (never sent to a server). The other device scans it,
types the code, and imports the same seed; both devices then resolve the
SAME owner address, so every subdomain shows and is controllable on every
device. The chain read (list_owned_tokens=ownerOf) already worked —
the fix was getting the same seed onto both devices, with no on-chain
pairing, no per-device keys, and no redirect/pointer glue. The prior
on-chain device-pairing path (PairingFacet,.lh_device_key,
ECIES-wrap-to-device) is superseded and dormant.
Fixed
- No-wallet claim no longer silently mints a second identity. A device
with no wallet that claimed a name used to auto-generate a fresh seed —
which is how a returning user on a second device ended up owning a
different EOA's subdomains, splitting their identity. The claim flow
now offers an explicit choice (create a new identity vs adopt an existing
one) instead of minting silently.
Changed
- Full on-chain reset (2026-06-01). A brand-new diamond,
$LH
token, and ERC-6551 infra were deployed; every prior address is
abandoned and balances do not migrate. Canonical addresses now
(Tempo Moderato, chain 42431, RPChttps://rpc.moderato.tempo.xyz):- Diamond (registry):
0x6c31c01e10C44f4813FffDC7D5e671c1b26Da30c $LHtoken (LocalharnessCredits):0x90B84c7234Aae89BadA7f69160B9901B9bc37B17- ERC-6551 registry:
0x2795810e5dfC8bC92Ef7fc9557F6c0699E11c3B3 - ERC-6551 account impl (MultiSignerAccount):
0x86be7c44d1940F4dE53A738153A12FaAEa68B5a7 - Credit proxy:
https://proxy-tau-ten-15.vercel.app
Facets currently cut into the diamond: DiamondCut, Loupe, Ownership,
LocalharnessRegistry, ERC721, Tba (MultiSigner impl), Feedback,
MainIdentity, Pairing (v2), Credits, Redeem, Session, CreditMeter,
X402, DeviceRegistry, Release. Registration is free; sessions are
free (duration 3600, price 0). Per-facet implementation addresses
are not pinned — they churn on re-cut; query the live set via the
DiamondLoupeFacet (facets()/facetAddress(selector)).
- Diamond (registry):
Added
- Credit proxy (
proxy/), deployed. A separate Vercel project
("proxy") athttps://proxy-tau-ten-15.vercel.app— a single
TypeScript Edge Function (proxy/api/gemini.ts) that holds the
platformGEMINI_API_KEYin env and is a transparent Gemini
passthrough. Auth is an Ethereum personal-sign in the
x-goog-api-keyheader (address:timestamp:signature); it gates on
an active SessionFacet session OR a CreditMeterFacet balance, and in
per-request mode debits via the meter key (viem, EIP-1559). The one
accepted off-chain component and the only server in the system;
everything else stays Tempo + browser. Platform credits are the
PRIMARY model; BYOK is the second option. - RedeemFacet — cut into the diamond.
Owner pre-loadskeccak256(code) -> $LHamounts via
addRedeemCodes(bytes32[],uint256);redeem(string)mints the mapped
$LHto the caller through the diamond'sISSUER_ROLE; owner-only
disableRedeemCodes. Bootstraps fresh wallets with zero off-chain
payment rails. Cut viascript/AddRedeemFacet.s.sol. - SessionFacet — cut into the diamond.
Coarse time-boxed$LHcredit sessions:openSession()spends
sessionPrice()$LHfor a window (expiry = now + sessionDuration()); owner-tunablesetSessionPrice/
setSessionDuration; viewsessionExpiryOf(address)the proxy gates
on. CurrentlysessionDuration = 3600,sessionPrice = 0(free in
beta). Cut viascript/AddSessionFacet.s.sol. - CreditMeterFacet — cut into the diamond. Fine-grained per-request
$LHmetering:depositCredits,creditOf, meter-onlymeter(...),
owner-onlysetMeter. The proxy's meter key EOA issetMeter'd +
funded. Cut viascript/AddCreditMeterFacet.s.sol. - X402Facet — cut into the diamond.
True x402 (EIP-712 "exact" scheme) payment settlement in$LHfor
agent-to-agent:settle(EOA ecrecover + EIP-1271 verify, one-shot
nonce),authorizationState,x402DomainSeparator(read it live —
the separator binds chainId + the diamond address).
The bundle's newsrc/x402_hook.rsinjects the EIP-712 signer into
call_agentso inter-agent calls settle in$LH. Cut via
script/AddX402Facet.s.sol. - DeviceRegistryFacet — cut into the diamond. Enumerable
linked-device index read in ONE call (linkDevice/unlinkDevice/devicesOf/
isDeviceLinked), replacingSignerAddedlog scraping that Tempo's
RPC caps at 100k blocks. Cut viascript/AddDeviceRegistryFacet.s.sol. - ReleaseFacet — cut into the diamond.
releaseName(tokenId): owner-only burn that frees a name for
re-registration; refuses the caller's MAIN. Cut via
script/AddReleaseFacet.s.sol. - Agent tools
list_subdomains+release_subdomain.
list_subdomains()enumerates the owner's holdings (read-only).
release_subdomain(name, confirmation)is DESTRUCTIVE — burns the
name via ReleaseFacet; it requiresconfirmation == name(typed in
chat), refuses the owner's MAIN, and is not given to subagents. The
system prompt now mandates a typed, never-auto-filled confirmation for
any destructive/irreversible action. src/registry.rsclient helpers for all of the above:
redeem_sponsored,open_session_sponsored,session_expiry_of,
session_price,credit_balance_of,deposit_credits_sponsored,
x402_domain_separator,x402_digest,sign_x402,
settle_x402_sponsored,x402_authorization_state,devices_of,
is_device_linked,consolidate_into_main_sponsored,
release_name_sponsored,release_name_calldata,erc20_balance_of,
andremove_signer_sponsored(now also unlinks the DeviceRegistry
index).