@0xsarwagya/ghost
v0.1.0
Published
Persistent cryptographic identity for web apps without accounts.
Maintainers
Readme
Ghost
Persistent cryptographic identity for web apps without accounts.
A TypeScript library that gives a browser a keypair instead of giving your application a user database. The private key never leaves the browser. The browser gets a stable Ghost ID, and the active public key becomes a credential for that Ghost. Your server verifies signatures.
Ghost authenticates a key, not a person.
Install
pnpm add @0xsarwagya/ghostFirst identity
In the browser:
import { createGhost } from "@0xsarwagya/ghost";
const ghost = await createGhost();
console.log(ghost.id); // ghost_1_crhgcniramqtgfpib5uiaautocwdigbt
console.log(ghost.credentialId); // cred_1_…
// Later, prove possession of a server-issued challenge:
const proof = await ghost.sign(challenge);On your server:
import {
createChallenge,
InMemoryChallengeStore,
InMemoryGhostCredentialStore,
verifyGhostProof,
} from "@0xsarwagya/ghost/server";
const store = new InMemoryChallengeStore();
const credentials = new InMemoryGhostCredentialStore();
credentials.register(ghost); // persist this in your own database in production
// 1. Issue a one-time challenge.
const challenge = createChallenge({ audience: "https://app.example", action: "login" });
// 2. Verify the browser's proof.
const result = await verifyGhostProof(proof, {
expectedAudience: "https://app.example",
challengeStore: store,
credentialStore: credentials,
});
if (result.ok) {
console.log("same ghost as before:", result.ghostId);
}No email. No password. No OAuth. No user table.
Optional recovery
Ghost is usable without recovery. When a Ghost owns durable value, ask the user whether to make it recoverable:
const { recoverySecret, recoveryRecord } = await ghost.enableRecovery();
// Show recoverySecret once. Store recoveryRecord with your app's Ghost row.If the browser profile is lost, the user supplies the secret and your app supplies the recovery record:
import { recoverGhost } from "@0xsarwagya/ghost";
const ghost = await recoverGhost({ recoverySecret, recoveryRecord });
// Same ghost.id, fresh non-extractable credential.Status
Experimental. The protocol is versioned and the v1 wire format is pinned by test vectors, but the API may still change before 1.0. Read the security model before relying on it — especially what Ghost does not prove.
Documentation
https://oss.sarwagya.wtf/ghost/docs
