From 99361eabd7d29916a77e970b826138f018d4f256 Mon Sep 17 00:00:00 2001 From: Eddie Date: Thu, 4 Jun 2026 22:35:47 -0400 Subject: [PATCH 1/4] chore: README + point at EffectStream wallet-passkeys + deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add README.md covering what the dApp does, the postMessage protocol it exchanges with the wallet, local-dev, build/deploy, and how this relates to the multi-chain effectstream-social-2of3-wallet project * Update src/components/fake-dapp.tsx: - PASSKEYS_ORIGIN now points at https://wallet-passkeys.ac-edward.workers.dev (the EffectStream-hosted wallet-passkeys worker), not rvcas's - Header copy: "EffectStream Demo App" (was "Fake dApp"), "Connected via wallet-passkeys" (was "Connected via passkeys.rvcas.dev") - Auth Card description: "EffectStream Wallet" / "Authenticating via wallet-passkeys" (replacing midnightOS / rvcas references) * Update wrangler.jsonc: - account_id: EffectStream (28ea08e3…) - name: wallet-passkeys-app so workers.dev URL matches the repo name - drop the upstream rvcas custom_domain route Live at https://wallet-passkeys-app.ac-edward.workers.dev --- README.md | 115 +++++++++++++++++++++++++++++++++++ src/components/fake-dapp.tsx | 12 ++-- wrangler.jsonc | 10 +-- 3 files changed, 124 insertions(+), 13 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..748a08b --- /dev/null +++ b/README.md @@ -0,0 +1,115 @@ +# wallet-passkeys-app + +A demo dApp that authenticates users via the EffectStream +[`wallet-passkeys`](https://github.com/effectstream/wallet-passkeys) worker. +Embeds the wallet's `/embed` route as a cross-origin iframe and uses the +documented `postMessage` RPC to register passkeys, sign in, and request +signatures — without ever holding any private key material itself. + +**Live deployment**: + +This is a fork of [`rvcas/fake-app`](https://github.com/rvcas/fake-app), +adapted to point at the EffectStream-hosted wallet-passkeys worker and to +deploy under the EffectStream Cloudflare account. + +## What it does + +A minimal third-party application that demonstrates how to integrate with +`wallet-passkeys` from a different origin: + +1. Mounts as a same-page + iframe inside a Card component. +2. Listens for `midnightos-passkeys` `postMessage` events from that iframe. +3. Sends `midnightos-dapp` requests back when the user clicks Register, Sign + In, or Sign Message. +4. Displays the returned `did:key` identity, the access key's public key, and + the signed message (raw `r||s` + DER ASN.1). + +The dApp itself stores no key material. Every credential, every signature, and +every byte of private key lives at the wallet-passkeys origin. The dApp only +sees public artifacts: the user's DID, the access key's public key, signatures. + +## Cross-origin postMessage protocol + +The iframe and parent exchange typed messages over `postMessage`: + +| Direction | `type` | Payload | +|---|---|---| +| dApp → wallet | `register` | `{ username }` | +| dApp → wallet | `sign-in` | `{ credentialId? }` | +| dApp → wallet | `sign` | `{ message, requestId }` | +| wallet → dApp | `ready` | `{}` | +| wallet → dApp | `authenticated` | `{ credential, did, didDocument, accessKeyPublicKey, keyAuthorization }` | +| wallet → dApp | `signed` | `{ requestId, message, signature, signatureDer, publicKey }` | +| wallet → dApp | `sign-error` / `error` | `{ requestId?, message }` | + +Each side discriminates messages by a `source` string (`midnightos-dapp` or +`midnightos-passkeys`) plus an origin check so neither will react to traffic +from an unrelated frame. + +## Local development + +```sh +pnpm install +pnpm dev +# Vite+ dev server at http://localhost:5173 (or whatever vp picks) +``` + +To run against a local wallet-passkeys instance instead of the deployed worker, +edit the `PASSKEYS_ORIGIN` constant in +[`src/components/fake-dapp.tsx`](./src/components/fake-dapp.tsx) — by default +it points at . + +## Build + deploy + +```sh +pnpm run build # tsc + vp build → dist/ +pnpm run deploy # builds + wrangler deploy +``` + +The deploy targets the EffectStream Cloudflare account +(`28ea08e36bc67a4f136df373255ce175`) via [wrangler.jsonc](./wrangler.jsonc). +The worker binding name is `wallet-passkeys-app`, so the workers.dev URL is +. + +## How to test + +1. Open in Chrome (or any + browser with platform passkey + WebAuthn support). +2. Click **Connect with Passkey**. The wallet-passkeys iframe appears. +3. Inside the iframe, click **Register**. Your OS shows the passkey-create + prompt (Touch ID, Windows Hello, Android biometric, etc). +4. Approve. The iframe completes the flow and posts your `did:key:z…` identity + back to this dApp; the iframe collapses and the Identity card appears. +5. Type a message into **Sign Message** and click the button. The dApp posts a + `sign` request to the iframe; the access key (held inside wallet-passkeys) + signs the message without re-prompting biometrics. The signed message + raw + and DER signatures appear in the UI. + +End-to-end, the user touched the OS biometric prompt once during Register and +once if they ever call `sign-in` again on a fresh session. Subsequent message +signatures are silent. + +## How this fits in EffectStream + +Together with [`wallet-passkeys`](https://github.com/effectstream/wallet-passkeys) +this pair is a reference implementation of the embed-and-postMessage pattern +EffectStream uses elsewhere — including the +[`effectstream-social-2of3-wallet`](https://github.com/effectstream/effectstream-social-2of3-wallet) +multi-chain wallet, which uses the same cross-origin iframe + postMessage +protocol shape for its EVM / Cardano / Midnight signing surface. + +The passkey design here is complementary, not competing: + +* **wallet-passkeys** — passkey is the *root* signing key, access key is a + delegated session signer. Single-curve (P-256 ECDSA), single identity (`did:key`). +* **effectstream-social-2of3-wallet** — passkey/Drive is the *unlock* for one + Shamir share; the actual signing keys are derived per-chain from a master + entropy. Multi-chain (EVM secp256k1, Cardano ed25519/Icarus, Midnight Zswap + + Dust + Night), one entropy per user. + +You can run them side-by-side or pick whichever model fits your app. + +## License + +Same as the upstream project. See [LICENSE](./LICENSE) once added. diff --git a/src/components/fake-dapp.tsx b/src/components/fake-dapp.tsx index 0cd7271..f4b2767 100644 --- a/src/components/fake-dapp.tsx +++ b/src/components/fake-dapp.tsx @@ -2,7 +2,9 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -const PASSKEYS_ORIGIN = "https://passkeys.rvcas.dev"; +// Points at the EffectStream-hosted wallet-passkeys worker. Override locally +// by editing this constant if you're running wallet-passkeys on localhost. +const PASSKEYS_ORIGIN = "https://wallet-passkeys.ac-edward.workers.dev"; const EMBED_URL = `${PASSKEYS_ORIGIN}/embed`; type KeyAuthorization = { @@ -120,10 +122,10 @@ export function FakeDapp() {
-

Fake dApp

+

EffectStream Demo App

{authResult - ? "Connected via passkeys.rvcas.dev" + ? "Connected via wallet-passkeys" : "A third-party application that authenticates via cross-origin iframe"}

@@ -154,8 +156,8 @@ export function FakeDapp() { {iframeMounted && ( - midnightOS Wallet - Authenticating via passkeys.rvcas.dev + EffectStream Wallet + Authenticating via wallet-passkeys {error && !authResult && ( diff --git a/wrangler.jsonc b/wrangler.jsonc index c850a30..9b4c6d1 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -1,13 +1,7 @@ { "$schema": "node_modules/wrangler/config-schema.json", - "name": "fake-app", - "account_id": "864bff2c217f01d65488f7f5d61c5e55", - "routes": [ - { - "pattern": "fake-app.rvcas.dev", - "custom_domain": true, - }, - ], + "name": "wallet-passkeys-app", + "account_id": "28ea08e36bc67a4f136df373255ce175", "compatibility_date": "2025-09-27", "observability": { "enabled": true, From 7084d41d2fa22b1abf355249eaea307ce41c88f0 Mon Sep 17 00:00:00 2001 From: Eddie Date: Thu, 4 Jun 2026 22:44:50 -0400 Subject: [PATCH 2/4] =?UTF-8?q?style:=20editorial/Bauhaus=20aesthetic=20?= =?UTF-8?q?=E2=80=94=20cream=20paper,=20bold=20orange,=20Space=20Grotesk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sister restyle to the green/black terminal look on wallet-passkeys. When the wallet iframe pops up inside this dApp the visual contrast (warm cream editorial layout on the outside, hard-edged dark terminal on the inside) makes the iframe boundary unambiguous to the user. Theme: * :root / .dark CSS variables redone in OKLCH: warm cream background, near black foreground, confident burnt-orange primary, bauhaus chart palette * --font-sans / --font-heading → "Space Grotesk" (CDN), --font-mono kept for monospace tags / chips * --radius: 0 — sharp corners everywhere New utility classes in style.css: * .es-wordmark — clamp(2.5rem,6vw,4.5rem) Space Grotesk display with .accent span coloring * .es-chip / .es-chip.primary — uppercase mono tags (black/cream or orange/cream) * .es-rule — orange ticker rule for section transitions * .wallet-popup-frame — heavy 3px black border + offset orange box-shadow on the iframe, with an "WALLET IFRAME · …workers.dev" tag callout, so the iframe reads as a separate object from the host page Component: * fake-dapp.tsx — header replaced with the EFFECT/STREAM/DEMO wordmark + a "Demo dApp · Issue 01" chip; iframe wrapped in .wallet-popup-frame; spacing / typography opened up to feel like a magazine spread * index.html — Space Grotesk via Google Fonts; title "EffectStream · Demo App" Builds clean. Deployed to https://wallet-passkeys-app.ac-edward.workers.dev --- index.html | 8 +- src/components/fake-dapp.tsx | 64 +++++----- src/style.css | 235 +++++++++++++++++++++++------------ 3 files changed, 196 insertions(+), 111 deletions(-) diff --git a/index.html b/index.html index 510b35e..43620c4 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,13 @@ - fake app + + + + EffectStream · Demo App
diff --git a/src/components/fake-dapp.tsx b/src/components/fake-dapp.tsx index f4b2767..4743a78 100644 --- a/src/components/fake-dapp.tsx +++ b/src/components/fake-dapp.tsx @@ -119,14 +119,19 @@ export function FakeDapp() { }, [messageToSign]); return ( -
-
-
-

EffectStream Demo App

-

+

+ {/* Editorial wordmark header */} +
+
+ Demo dApp · Issue 01 +

+ EFFECTSTREAM +
DEMO. +

+

{authResult - ? "Connected via wallet-passkeys" - : "A third-party application that authenticates via cross-origin iframe"} + ? "Connected · access key issued by wallet-passkeys" + : "A consumer application that authenticates users through a cross-origin wallet iframe. This page holds no key material."}

{authResult && ( @@ -135,6 +140,7 @@ export function FakeDapp() { )}
+
{/* Connect button — shown before iframe is mounted */} {!iframeMounted && !authResult && ( @@ -152,36 +158,32 @@ export function FakeDapp() { )} - {/* Auth iframe — visible during auth, hidden after */} + {/* Auth iframe — wrapped in an unmistakable "wallet popup" frame so + the boundary between this page and the wallet origin is obvious. */} {iframeMounted && ( - - - EffectStream Wallet - Authenticating via wallet-passkeys - - - {error && !authResult && ( -

{error}

- )} +
+ {error && !authResult && ( +

{error}

+ )} +