95 lines
4.9 KiB
Markdown
95 lines
4.9 KiB
Markdown
# Goostr (static)
|
|
|
|
Your Google account is your nostr identity. No server, no key management.
|
|
|
|
**Pure static site.** No build step. No backend. No install required to run.
|
|
|
|
## How it works
|
|
|
|
1. User clicks **Sign in with Google**. We request three OAuth scopes: `profile`, `email`, and `drive.appdata`.
|
|
2. We look in the user's **Google Drive App Data folder** for a file `goostr-key-v1.json`. (App Data is a per-OAuth-client hidden folder in the user's Drive that only this app's client ID can read. WhatsApp uses it for backups; Enpass uses it for vault sync.)
|
|
- **If found** → download it. The 32 bytes inside are the nostr private key. User is now signed into their existing nostr identity.
|
|
- **If missing** → generate 32 fresh random bytes (`crypto.getRandomValues`), upload to App Data. User now has a brand-new nostr identity.
|
|
3. We fetch the user's Google name + picture. For a brand-new identity we publish a kind-0 metadata event with them so it has a profile. For an existing one, if no relay returns a kind-0 within ~6s, we publish it then.
|
|
4. SessionStorage caches the key inside the tab (max 12h) so reloads don't require re-auth. Closing the tab logs out.
|
|
|
|
Note: the key is **stored**, not derived. It's random bytes that live in the Drive file. Delete that file (or revoke/delete the OAuth client, which makes App Data unreadable) and the identity is gone for good.
|
|
|
|
### The demo app
|
|
|
|
Once signed in, [`app.js`](public/app.js) shows your name, avatar and npub, a dot per relay (connecting / open / closed / timeout), your last 50 notes (kind 1), and a box to publish a new note.
|
|
|
|
### Relays
|
|
|
|
[`relay.js`](public/relay.js) opens every relay in [`config.js`](public/config.js) in parallel (4s timeout each) and lets the UI continue as soon as **one** is open. Relays that connect later are sent every active subscription plus anything published in the last 60s, so nothing is lost to a slow relay.
|
|
|
|
### Why this is good
|
|
|
|
- Works across devices: sign in on phone, laptop, anything — same Google account → same nostr identity.
|
|
- No nsec to lose, no seed phrase to back up. Recovery == "remember your Google password."
|
|
- The seed key never touches *this* device's disk. It lives in Google Drive (encrypted at rest by Google) and in tab-only sessionStorage.
|
|
|
|
### What this is *not*
|
|
|
|
- Not as secure as `keystr` (passkey-based). Anyone with your Google password + 2FA can read your nostr key. With keystr/passkeys, the user must physically be at the authenticator device.
|
|
- Not for situations where you want to be invisible to a single big platform — Google obviously knows you own this Drive App Data file.
|
|
- Not safe against script injection. While signed in, the raw key sits in `sessionStorage`, readable by any script on the page. That includes `nostr-tools`, loaded from `esm.sh` via the import map in `index.html` with no integrity check. Pin/self-host it before trusting this with a real identity.
|
|
- Every sign-in shows Google's consent screen (`prompt: "consent"`) unless `signIn({ silent: true })` is used.
|
|
|
|
## One-time Google setup (~2 minutes)
|
|
|
|
1. Open <https://console.cloud.google.com/apis/credentials>.
|
|
2. Create or select a project.
|
|
3. Enable the **Google Drive API** under "Library."
|
|
4. Under "OAuth consent screen", configure a minimal app (External + Testing is fine for prototyping; add yourself as a Test User).
|
|
5. Back in "Credentials" → **Create Credentials** → **OAuth client ID** → **Web application**.
|
|
6. Under **Authorized JavaScript origins** add:
|
|
- `http://localhost:5174` (for local dev)
|
|
- your deploy URL (e.g. `https://you.github.io`)
|
|
7. Copy the **Client ID** — looks like `1234567890-abcdef.apps.googleusercontent.com`.
|
|
8. Paste it into [`public/config.js`](public/config.js) as the value of `CLIENT_ID`.
|
|
|
|
## Run it
|
|
|
|
```bash
|
|
python3 -m http.server -d public 5174
|
|
# or
|
|
npx serve public -l 5174
|
|
```
|
|
|
|
Then open <http://localhost:5174>. Google requires HTTPS for OAuth in production, but `localhost` is exempt for dev.
|
|
|
|
## Deploy it
|
|
|
|
Static folder. Drop `public/` into any host. GitHub Pages, Netlify, Vercel, Cloudflare Pages, S3 — all fine. Make sure HTTPS is on, and add your deployed URL to the Google Cloud OAuth client's authorized origins.
|
|
|
|
## Layout
|
|
|
|
```
|
|
public/
|
|
index.html — entry point + setup banner
|
|
app.js — UI glue
|
|
goostr.js — library: signIn(), restoreSession(), clearSession()
|
|
drive.js — Google Drive App Data helpers
|
|
relay.js — minimal nostr WebSocket pool with replay-on-late-connect
|
|
style.css — dark UI
|
|
config.js — CLIENT_ID + relay list (edit this)
|
|
```
|
|
|
|
## Library use
|
|
|
|
```js
|
|
import { signIn, restoreSession, clearSession } from "./goostr.js";
|
|
|
|
// On boot, try restoring first (sessionStorage):
|
|
let session = restoreSession();
|
|
if (!session) {
|
|
session = await signIn({
|
|
clientId: "1234.apps.googleusercontent.com",
|
|
onStatus: (msg) => console.log(msg),
|
|
});
|
|
}
|
|
const { signer, profile } = session;
|
|
// signer.sign({ kind: 1, content: "hello nostr", tags: [] })
|
|
```
|