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
2026-09-27 08:37:10 -06:00
2026-09-27 08:37:10 -06:00
2026-09-27 08:37:10 -06:00
2026-09-27 08:37:10 -06:00

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 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 opens every relay in 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 as the value of CLIENT_ID.

Run it

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

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: [] })
Description
Nostr with Goog
Readme 186 KiB
Languages
JavaScript 75.4%
HTML 12.4%
CSS 10.6%
Shell 1.6%