experimental@0xsarwagya/ghost
Why does every tiny app need an account?
I refuse to build an account system for an application that does not need to know who you are.
Most applications never needed your name, your email, or an identity provider. They needed continuity: is this the same browser that was here yesterday? Ghost answers exactly that. The browser generates a keypair and keeps the private half where JavaScript cannot read it. The Ghost ID is the stable identity; the public key is the active credential. Your server verifies signatures. There is no user table, and no Ghost service — that absence is the product.
pnpm add @0xsarwagya/ghost# or: npm install @0xsarwagya/ghostThe whole API
// browser
import { createGhost } from "@0xsarwagya/ghost";
const ghost = await createGhost();
ghost.id; // ghost_1_crhgcniramqtgfpib5uiaautocwdigbt
ghost.credentialId; // cred_1_…
const proof = await ghost.sign(challenge);
// your server
import { createChallenge, verifyGhostProof } from "@0xsarwagya/ghost/server";
const challenge = createChallenge({ audience: "https://app.example", action: "login" });
const result = await verifyGhostProof(proof, {
expectedAudience: "https://app.example",
challengeStore: store,
});
// result.ok → the same ghost is backFirst visit, every return
No email. No password. No OAuth. No user database.
Ghost authenticates a key, not a person.
That sentence is the security model. A verified proof means the requester holds an active credential for this identity — not that they are a human, a unique individual, or anyone in particular. Identities are origin-bound and pairwise, so nothing follows anyone across the internet. Recovery is optional and user-held, without accounts.
Compatibility
Reported from what CI actually exercises in Chromium, Firefox, and WebKit — including the load-bearing assumption that a non-extractable key survives IndexedDB across a reload.
| Feature | Chromium | Firefox | WebKit |
|---|---|---|---|
| Non-extractable Ed25519 identity | Tested in CI | Tested in CI | Tested in CI |
| Key persists across reloads | Tested in CI | Tested in CI | Tested in CI |
| Sign in browser, verify on server | Tested in CI | Tested in CI | Tested in CI |
| Web Crypto Ed25519 baseline: Chrome 137+, Firefox 129+, Safari 17+. Older browsers get a typed UNSUPPORTED, never a silent fallback. | |||
Used in Local
Local uses Ghost as the identity primitive between two chatting browsers. Each peer signs a nonce chosen by the other; the conversation history is pinned to the peer's Ghost ID, and reconnecting with a mismatched public key refuses to attach silently. Source: github.com/0xsarwagya/local.