20 Commits
Author SHA1 Message Date
zeekay b0e0d789f9 chore: sync working tree
Commits 4 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 10:06:48 -07:00
zeekayandHanzo Dev 75c529deb8 fix(wallet-web): visible brand marks, centered auth card, spaced CTAs, no cross-brand chain leak
- brands/{lux,zoo,hanzo}.svg: real marks (violet Lux prism, colorful Zoo venn,
  Hanzo block-H). The prior rect fill="currentColor" + black glyph collapsed to a
  solid black square in the light-theme header (currentColor is the dark text color).
- gui-stub tokenize(): numeric Tamagui space/size tokens ($3/$6/$10) now resolve to
  px on a 4px scale. They were mapped to undefined CSS vars (var(--3)), so every
  gap/padding/fontSize was silently dropped — the source of the gap-less "button
  blob" (colliding radii) and the top-anchored auth card.
- Welcome: plain inline-styled, viewport-centered card with a max-width panel, the
  brand mark, and 44px CTAs separated by real gaps (matches AppShell/Callback pattern,
  independent of the unresolved Tamagui token contract).
- overlays/{lux,zoo,hanzo}/brand.json: drop sibling first-party L1s from the chain
  selector. Zoo no longer lists "Lux C-Chain"/"Hanzo"; each brand keeps its own
  networks + neutral public EVMs (Ethereum/Arbitrum/Base/Polygon/Avalanche).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-19 01:30:36 -07:00
zeekayandHanzo Dev 6dc2c4e9b4 deploy(wallet-web): pin lux overlay to 21986289d (font + overflow fixes)
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 23:47:10 -07:00
zeekayandHanzo Dev 21986289db fix(web): global sans-serif font — wallet was rendering in UA serif (Times)
The SPA ships no font files and never set a font-family, so every element fell
back to the browser's serif default (Times) — the landing page read as
"unstyled" even though the brand theme (accent/surface/neutral CSS vars) was
being injected correctly by loadBrandConfig. Set a system sans-serif stack on
html/body (zero bundled assets, crisp on every platform) and force form controls
(button/select/input) to inherit it, since they don't by default.

Verified headless (Chrome channel, 390px): body font-family was "Times" before;
this makes it the system sans stack. Pairs with the prior overflow fix.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 23:46:52 -07:00
zeekayandHanzo Dev 5dd2b16022 fix(web): stop mobile horizontal overflow on the wallet landing page
At 390px the top bar was a single non-wrapping flex row whose min-content
width (~470px) forced the single-column app grid — and thus the whole
document — to 478px, pushing the account/connect control off-screen and
clipping the CTA card. Two changes:

- index.html: add the app's one document-level reset (box-sizing:border-box
  + zero html/body margin). The missing UA body margin reset was adding the
  extra 8px (478 vs 470). No overflow-x:hidden — it would break the sticky
  header.
- AppShell.tsx: let the header wrap (flex-wrap) so the chain+account group
  drops to a second row instead of widening the document; grid header row
  becomes minmax(56px,auto) so the wrapped row isn't clipped; groups get
  min-width:0 so they can shrink.

Verified headless at 390x844: document.scrollWidth 478 -> 390, zero
elements past the viewport, connect control + CTA fully on-screen.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 20:48:55 -07:00
zeekayandHanzo Dev db275f7487 deploy(wallet-web): lux overlay tag-pin fix + plain-manifest fallback
Pin the image tag the way a custom CR requires: the lux.cloud/v1 Service CR
holds the tag in structured spec.image.tag, which the kustomize images:
transformer does NOT reach — so it is pinned via a JSON patch (was silently
staying at :latest). Set to the built sha dc08a6af0.

Add fallback.yaml: equivalent plain manifests (Deployment + Service + Ingress +
cert-manager Certificate via ingress-shim / letsencrypt-prod DNS-01). lux-k8s
runs lux-operator scaled to 0, so services.lux.cloud does not reconcile; these
reproduce exactly what it would generate, letting wallet.lux.network go live
without scaling the operator (which would touch other lux-mainnet CRs/luxd).
Follows the proven safe.lux.network ingress pattern (class ingress, DNS-01).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 14:37:11 -07:00
zeekayandHanzo Dev dc08a6af0d fix(web): copy root tsconfig.base.json into Docker build stage
The apps/web Dockerfile selectively COPYs workspace sources but omitted the
root tsconfig.base.json that pkgs/{wallet,brand,analytics,chains} each `extends`.
Vite 8's rolldown/oxc TS transform resolves the extends chain at build time and
hard-fails ("Tsconfig not found /app/tsconfig.base.json", surfaced only as the
opaque `errors: [Getter/Setter]`) when the base is absent. Local vite build
masks it (runs against the full repo tree where the base exists); every
containerized build failed regardless of OS/arch. Reproduced in node:22-alpine
and verified fixed — dist/ now produced.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 14:21:38 -07:00
zeekayandHanzo Dev 5754513482 fix(web): bridge onChangeText->onChange in gui-stub Input so onboarding accepts input
The auth screens (ImportMnemonic/SetPIN/Unlock/ConfirmMnemonic) speak the
Tamagui/RN text-input contract (onChangeText, secureTextEntry, multiline,
onSubmitEditing) - the same contract @luxfi/ui@7.4.0's bridged Input honors.
The local @hanzo/gui stub spread these raw onto a native <input>, so a
value + onChangeText field was controlled with no working handler and
silently dropped every keystroke (dead seed-phrase / PIN entry). Translate
the contract to native web events in the stub.

Verified 390x844: import-phrase textarea + PIN + unlock accept keystrokes,
create/import persists, unlock reaches /portfolio, quick-actions soft-nav
without session drop.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 10:50:45 -07:00
zeekayandHanzo Dev 1ddc0df258 fix(web): dashboard quick-actions use SPA soft-nav so the session survives
The wallet keeps `isUnlocked` + the plaintext mnemonic in memory ONLY (never
persisted — see the auth store's partialize), so ANY full-document reload wipes
the session and bounces the user back to onboarding. On mobile this made the
wallet unusable: every full-reload dashboard action effectively logged you out.

- Portfolio dashboard: render the Send / Cross-Chain / Earn / Manage Keys
  quick-action cards as react-router <Link> (client-side soft-nav) instead of
  full-reload <a href>. A tap pushState-navigates and the in-memory unlock +
  mnemonic survive.
- Audit for other session-dropping full-reload links: Callback's "Back to
  wallet" recovery link was a raw <a href="/"> — converted to <Link to="/">.

Verified at 390x844 (iPhone-class): the 4 cards render with no horizontal
overflow (documentElement.scrollWidth == 390); tapping Send soft-navigates to
/send with NO document reload (a page sentinel + the zustand auth store both
survive, isUnlocked stays true); a full-reload navigation (the prior behavior)
wipes the in-memory session — confirming the fix.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-18 10:43:17 -07:00
zeekay 83e2706fb6 remove Stripe from onramp deep-link test fixtures (Stripe account disabled)
Stripe is no longer an available fiat on-ramp provider. Drop it from the
buy-link provider test fixtures; moonpay+coinbasepay still exercise the
multi-provider parse and case-normalization paths.
2026-06-24 12:34:20 -07:00
zeekay a86efad105 chore(deps): lockfile → @luxwallet/* from npm (no file:) 2026-06-24 11:16:40 -07:00
zeekay 48915e47db chore(deps): @luxwallet/chains + @hanzo/iam file: → published npm 2026-06-24 11:11:51 -07:00
zeekay 9d6c1d9a0c feat(web): chain registry from @luxwallet/chains + IAM login via @hanzo/iam
Chain metadata is now sourced from the canonical @luxwallet/chains registry
(MIT) instead of three duplicated hardcoded maps:
  - new lib/chains.ts adapter: chainLabel / evmChainDef / evmChainIds /
    registryChains, with a UI-only block-explorer overlay.
  - config/wagmi.ts, components/ChainSwitcher.tsx, lib/brand.ts all read it.
  - the wallet now recognises/holds every Lux Bridge chain: Lux C/X/P/Q/Z,
    sovereign L1s (Hanzo/Zoo/Pars/Sparkle Pony), external EVM (Ethereum,
    Arbitrum, Base, Polygon, Optimism, Avalanche) and non-EVM bridge families
    (Bitcoin, Solana, TON, XRP, Polkadot). brand.supportedChainIds still
    narrows the active wagmi/switcher surface per white-label.

IAM account login is delegated to the canonical @hanzo/iam SDK (MIT, HIP-0111
one-way auth) — lib/iam.ts is now a thin brand-aware facade over
@hanzo/iam/browser, keeping its existing surface (beginLogin/completeLogin/
getAccessToken/hasSession/clearSession) so store/session, lib/custody and the
callback route are unchanged. Adds a 'Sign in with <brand>' button to the
Welcome screen (additive, alongside local create/import).

The wallet's own keyring + signing path is untouched: this is the chain
metadata source + IAM account login, not external-wallet-connect.

Deps (file: for now, TODO npm): @luxwallet/chains, @hanzo/iam — both MIT,
zero new GPL.
2026-06-24 09:15:08 -07:00
zeekay 82e0d5512e fix(web): re-sync store chainId to brand.defaultChainId after loadBrandConfig (white-label default chain) 2026-06-23 16:27:05 -07:00
zeekay 3b1ad33433 test(backend): LIVE backend↔MPC custody e2e (not mock)
The not-mock counterpart to internal/api/e2e_test.go (which wires the real
custody adapter to a MOCK lux/mpc). This drives the wallet backend's /v1 API
against a REAL 3-node mpcd ensemble (consensus mode, the production code path)
over the live HTTP /keygen + /sign endpoints.

Path (a) full-OIDC: a local RSA-signed OIDC issuer + the real iam.Verifier
(standard discovery + JWKS + RS256 verify) + real org-scoping (owner claim) +
the real api.Server /v1 handlers + custody.NewMPC pointed at live node0. Proves
the full chain: lux.id verify → org scope → LIVE MPC threshold keygen + sign,
with no plaintext key in any backend response or MPC node log.

Exercises POST /v1/wallets (live keygen → real evm address), GET /v1/wallets,
POST /v1/wallets/{id}/sign (live threshold signature), backend-level idempotency
(same key → same signature), and the missing-idempotencyKey → 400 boundary.

//go:build integration. Builds mpcd from the lux/mpc repo (LUX_MPC_REPO, default
/Users/z/work/lux/mpc); skips cleanly if that repo is absent. Reuses the OIDC
helpers (newIDP/tokenForOrg/do) from api_test.go; stdlib-only ids (no new dep).

Run: go test -tags integration ./internal/api/ -run TestBackendLiveMPC -v
2026-06-23 16:24:27 -07:00
zeekay 611a3d77da docs(LLM): extend App(Wallet) spec — native binaries + download deployables, per-brand chainDefault, live MPC /sign 2026-06-23 16:00:04 -07:00
zeekay 10c926c22a feat(web): white-label custody wiring + per-brand deploy + download page 2026-06-23 15:51:02 -07:00
zeekay 64bd0f67c9 feat(backend): App(Wallet) MPC custody server (Go, no plaintext keys)
Add apps/backend — the wallet's custody server. The load-bearing Front-E gap
(repo was FE-only) is now closed: a wallet account model backed by MPC custody
where the private key is split t-of-n across the MPC nodes (lux →
mpc.lux.network) and NEVER assembled — this service stores only public keys +
addresses.

- internal/iam: lux.id OIDC bearer verification via JWKS (discovery + RS256/
  ES256, alg-confusion-safe — HMAC/none refused). Org from the 'owner' claim.
  golang-jwt/v5 (matches the lux ecosystem), stdlib JWKS fetcher (no extra dep).
- internal/custody: the Custodian port (a value the wallet owns) + HTTP adapter
  to lux/mpc's verified /keygen + /sign wire. Mirrors mpc client.Threshold's
  security contract (idempotency required, org-scoped). PQ-ready: mldsa65 is a
  first-class scheme (the PQ primitives stay in pkgs/wallet/.../pq + consensus —
  not duplicated). No in-process key fallback — unconfigured MPC is an error.
- internal/store: wallet↔org↔addresses (no key field). Org-scoped reads — a
  cross-org get is ErrNotFound (no existence disclosure).
- internal/api: /v1/* (no /api/ prefix). IAM-gated, org-scoped. POST/GET
  /v1/wallets, GET /v1/wallets/{id}, POST /v1/wallets/{id}/sign, GET /v1/health.
- cmd/wallet-backend: env config (white-label per tenant: IAM_ISSUER/AUDIENCE,
  MPC_ENDPOINT — shared hanzo-mpc or BYOMPC), JSON logs, graceful shutdown.

Deploy (native CI + operator, NO GHA): build-only .platform.yml → ghcr.io/
luxfi/wallet-backend on lux-build pools; lux operator rolls
apps/backend/k8s/wallet-backend.yaml (lux.cloud/v1 Service + KMSSecret for
MPC_SERVICE_TOKEN), ingress wallet-api.lux.network.

17 tests green (go test ./...): IAM (valid/no-org/wrong-aud/expired/alg-
confusion), custody (no-key-leak, idempotency-required, PQ-accepted, no-
fallback), API (auth-gate, org-isolation cross-org→404, create→sign), + a full
E2E through the real adapter→mock-MPC→real-server. Binary boots, /v1/health ok,
/v1/wallets 401 unauth. LLM.md captures the App(Wallet) spec for the unified
operator's App Kind.
2026-06-23 15:07:40 -07:00
zeekay fc08afc683 corona→corona: academic Corona now only in lp-220-p3q-corona 2026-06-11 10:28:44 -07:00
zeekay 0cc382741c scrub Liquidity branding + corona→corona (OSS brand hygiene) 2026-06-11 00:01:24 -07:00