Googstr/README.md
Jason Tudisco 21093eb800 Initial commit: Goostr static site
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:37:10 -06:00

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: [] })
```