Stake a timestamped, identity-bound claim on your ideas, works, IP, quotes, phrases, or any text by recording them on the BSV blockchain as PushDrop tokens.
Core promise: "This exact content was known to this identity at this block height."
- Write your stamp — an idea, quote, phrase, or any text you want to timestamp immutably on the blockchain
- Choose visibility — Public (text visible to everyone) or Sealed (only the SHA-256 hash is stored, content stays private)
- Stamp it — Typestamp hashes the content and records a PushDrop token on BSV
- Share the certificate link — anyone can verify the stamp without a wallet
- Verify — prove knowledge of sealed content by matching the original text against the on-chain hash at
/verify - Manage — toggle public/private visibility or delete stamps from My Stamps
- Public stamps — text visible on the overlay network table and certificate page
- Sealed stamps — content hidden, only the hash is stored on-chain and in the database. Prove knowledge via the verify page.
- Overlay Network page — live feed of all stamps indexed by independent overlay nodes, with auto-refresh every 30 seconds
- Overlay node discovery — nodes advertise via SHIP protocol and are discovered automatically; the app health-checks and displays all active nodes
- Certificate page — shareable, server-rendered page with OG tags for any stamp
- Verify page — paste any text to check if it matches a stamp on-chain, with public/sealed explainer cards and match/no-match result states
- Duplicate detection — identical text cannot be stamped twice (checked against both overlay and app DB)
- Identity-bound — each stamp is locked to the creator's identity key via BRC-42 key derivation
- Block height caching — block heights fetched from WhatsOnChain and cached in MongoDB
- Delete stamps — owner can delete a stamp, freeing the text for others to claim
- Dark/light theme — toggle between themes
- Home page teaser — 3 most recent stamps with relative time, linking to the full overlay table
- Next.js 15 — App Router, React 19, TypeScript, Tailwind CSS
- @bsv/sdk — WalletClient, PushDrop, BRC-42 key derivation
- @bsv/overlay-express — Overlay server with custom topic manager and lookup service
- MongoDB Atlas — stamp metadata, overlay indexing, duplicate detection
- WhatsOnChain API — on-chain transaction verification, block height lookups, UTXO fetching
- Node.js 18+
- A MongoDB Atlas cluster (or local MongoDB)
- A BSV wallet extension (e.g. Yours Wallet) for creating stamps
npm install
cd overlay && npm install && cd ..Create a .env.local file in the project root:
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/typestamp?retryWrites=true&w=majority
NEXT_PUBLIC_WOC_BASE=https://api.whatsonchain.com/v1/bsv/main
NEXT_PUBLIC_APP_URL=http://localhost:3000
OVERLAY_URL=http://localhost:8080
OVERLAY_KNOWN_NODES= # comma-separated fallback overlay URLs (optional, used if SHIP discovery returns empty)
Create an overlay/.env file:
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/typestamp?retryWrites=true&w=majority
OVERLAY_PRIVATE_KEY=<64-char hex private key>
OVERLAY_HOSTING_URL=http://localhost:8080
OVERLAY_PORT=8080
OVERLAY_PEER_URLS= # comma-separated peer node URLs for GASP sync + cross-submission (optional)
KNEX_URL= # external DB for engine state, e.g. mysql://user:pass@host:port/db (optional, defaults to SQLite)
# Run Next.js + Overlay server together
npm run dev:allThis starts the Next.js app on port 3000 and the overlay server on port 8080 via concurrently.
Open http://localhost:3000.
app/
page.tsx # Home — stamp form + recent stamps teaser
c/[txid]/page.tsx # Certificate page (SSR + OG tags)
overlaynetwork/page.tsx # Overlay Network — live feed from overlay nodes
verify/page.tsx # Verify a stamp (no wallet needed)
mytypestamps/page.tsx # User's stamps + visibility toggle
api/typestamps/ # POST (create + submit BEEF to overlay) + GET (list)
api/typestamps/[txid]/ # GET (single) + PATCH (visibility) + DELETE
api/typestamps/check/ # GET (duplicate check)
api/overlay/check/ # GET (duplicate check via overlay)
api/overlay/stamps/ # GET (paginated stamps from overlay, author join, block heights)
components/
WalletProvider.tsx # React context for wallet state
Header.tsx # Nav bar + wallet connect + theme toggle
StampForm.tsx # Content input + public/sealed mode + stamp flow
RecentStampsTeaser.tsx # Home page — 3 most recent stamps with relative time
NetworkFeed.tsx # Overlay Network feed with auto-refresh + pagination
CertificateCard.tsx # Certificate display + share + delete
ShareButtons.tsx # X + LinkedIn share
VerifyForm.tsx # Verify content against on-chain hash
MyTypeStampsList.tsx # User's stamps with search, hash copy, visibility toggle
ThemeToggle.tsx # Dark/light mode toggle
Footer.tsx # Site footer
overlay/
OverlayStampTable.tsx # 6-column table (Typestamp, Author, Identity Key, TXID, Block, Created At)
OverlayStats.tsx # Active nodes, stamps indexed, current block cards
OverlayHero.tsx # Overlay page header with live connection indicator
OverlayEducation.tsx # Animated flow diagram: BSV Blockchain → Overlay Node → App
OverlayNodePanel.tsx # Connected overlay nodes with health status
OverlayTrustBanner.tsx # Trust/immutability banner
OverlayPagination.tsx # Pagination controls
types.ts # OverlayStamp interface
lib/
mongodb.ts # MongoDB connection singleton
hash.ts # SHA-256 via Web Crypto API
wallet.ts # WalletClient singleton + helpers
attest.ts # PushDrop token creation
verify.ts # WhatsOnChain fetch + PushDrop decode
share.ts # Social share URL builders
overlay-discovery.ts # SHIP-based overlay node discovery + health checks
overlay/
src/index.ts # Overlay server entry (OverlayExpress + DirectAdvertiser)
src/DirectAdvertiser.ts # Custom SHIP/SLAP advertiser (bypasses Dojo, spends P2PKH directly)
src/TypeStampTopicManager.ts # Validates PushDrop outputs with typestamp protocol
src/TypeStampLookupService.ts# Indexes admitted outputs into MongoDB
src/TypeStampStorage.ts # MongoDB storage with upsert + indexes
Dockerfile # Container build for overlay server
Each stamp is a PushDrop token with four fields:
| Field | Value |
|---|---|
| Protocol | typestamp |
| Hash | sha256:<hex> |
| Title | First 100 chars of content (or Sealed Stamp) |
| Timestamp | Unix timestamp |
The token is locked to a derived key (BRC-42, protocol [0, 'typestamp']), signed, and stored in the typestamp basket.
The overlay network provides decentralized indexing — anyone can run a node and see the same data independently.
When a stamp is created, the raw transaction (BEEF) is submitted to all discovered overlay nodes. Each overlay node:
- Topic Manager (
tm_typestamp) validates the PushDrop output matches the typestamp protocol - Lookup Service (
ls_typestamp) indexes admitted outputs into MongoDB with decoded fields (hash, title, timestamp, identity key) - Lookup queries support
findAll,findByHash, andfindByIdentityKey
Overlay nodes advertise themselves via the SHIP protocol. The app queries the bootstrap node's ls_ship service for advertisements with tm_typestamp topic, decodes the PushDrop outputs to extract domain URLs, and health-checks each one. Results are cached for 30 seconds.
A fallback OVERLAY_KNOWN_NODES environment variable (comma-separated URLs) ensures nodes are discoverable even before SHIP advertisements propagate.
The default OverlayExpress advertiser (LegacyNinjaAdvertiser) connects to a Dojo wallet backend that can't see P2PKH UTXOs. Typestamp uses a custom DirectAdvertiser that bypasses Dojo entirely:
- Derives the identity key from the overlay's private key
- Fetches confirmed P2PKH UTXOs from WhatsOnChain for the identity address
- Builds PushDrop advertisement outputs (SHIP for topic managers, SLAP for lookup services) using BRC-42 key derivation
- Signs and broadcasts the transaction via WhatsOnChain
- Cross-submits the tagged BEEF to peer overlay nodes (configured via
OVERLAY_PEER_URLS) with the requiredX-Topicsheader
Advertisements are created on each startup once UTXOs are confirmed. The default SDK broadcaster (TopicBroadcaster) is replaced with a no-op to prevent OOM from broadcasting to every SHIP-discovered peer on the global network.
Overlay nodes synchronize their tm_typestamp data with each other using the GASP (Graph-Aware Sync Protocol). The sync approach works around several OverlayExpress limitations:
- GASP is disabled in config (
configureEnableGASPSync(false)) to preventstart()from blocking the HTTP listener while syncing - GASP routes are registered manually (
/requestSyncResponseand/requestForeignGASPNode) with explicitexpress.json()middleware, becausestart()addsbodyParserafter route registration - The default advertiser and broadcaster are removed before
start()to prevent theLegacyNinjaAdvertiserandTopicBroadcasterfrom pulling massive data from the global overlay network - Background sync: after the HTTP listener is up, a
setImmediatecallback creates advertisements viaDirectAdvertiser, submits them to the local engine, then runsengine.startGASPSync()scoped totm_typestamponly
On each restart, nodes pull new UTXOs from their configured peers. The sync is unidirectional per restart — Node A pulls from Node B, and Node B pulls from Node A when it restarts. The engine stores full BEEF data for each output so that GASP can serve raw transactions and merkle proofs to requesting peers.
Sync configuration in overlay/.env:
OVERLAY_PEER_URLS=https://other-node.example.com # comma-separated peer URLs
| Method | Route | Description |
|---|---|---|
POST |
/api/typestamps |
Create a stamp + submit BEEF to overlay nodes |
GET |
/api/typestamps |
List stamps (by identity key or public, paginated) |
GET |
/api/typestamps/[txid] |
Get a single stamp |
PATCH |
/api/typestamps/[txid] |
Toggle visibility or hide stamp (owner only) |
DELETE |
/api/typestamps/[txid] |
Delete a stamp (owner only) |
GET |
/api/typestamps/check |
Check for duplicate hash |
GET |
/api/overlay/check |
Check overlay + app collection for duplicate |
GET |
/api/overlay/stamps |
Paginated stamps from overlay (joins author, caches block heights, discovers nodes) |
| Collection | Purpose |
|---|---|
typestamps |
App-level stamp records (content, visibility, display name) |
overlay_typestamps |
Overlay-indexed stamps (decoded from on-chain PushDrop tokens) |
The overlay server also uses SQLite (or MySQL/PostgreSQL via KNEX_URL) for internal overlay engine state (SHIP/SLAP records, UTXO tracking).
npm run buildDeploy to Vercel and set environment variables (MONGODB_URI, NEXT_PUBLIC_WOC_BASE, NEXT_PUBLIC_APP_URL, OVERLAY_URL).
Deploy from the overlay/ directory:
cd overlay
railway upOr use the Dockerfile directly. Each overlay node needs its own OVERLAY_PRIVATE_KEY, OVERLAY_HOSTING_URL, and MONGODB_URI. Set OVERLAY_PEER_URLS to point to other nodes for cross-discovery and GASP sync.
Important: For GASP sync to work across deploys, use an external database for the overlay engine's internal storage by setting KNEX_URL (e.g. mysql://...). Without it, the engine defaults to SQLite which is ephemeral on container platforms like Railway, meaning BEEF data is lost on each deploy and GASP can't serve historical outputs to peers.