{"slug": "i-put-gopher-on-nostr-now-every-npub-is-a-gopherhole", "title": "I put Gopher on Nostr. Now every npub is a gopherhole", "summary": "A developer has built GopherKind, a publisher and reader that stores gopherholes as signed Nostr events, turning every Nostr public key into a gopherhole. The tool generates Gopher menus from a user's existing Nostr activity and supports publishing path-addressed pages as kind 31436 events, with a specification proposed as a NIP.", "body_md": "I've built [GopherKind](https://github.com/forgesworn/gopherkind), a publisher and reader for gopherholes stored as signed Nostr events.\n\nEvery Nostr public key already works as a hole. GopherKind turns the account's profile, notes, replies, long-form articles, and the follows and followers visible through its relays into ordinary Gopher menus and text. An author can then publish proper path-addressed pages as kind `31436`\n\nevents, and those pages take over the same hole.\n\nThe same content is readable through Gopher, Gemini, the web and a terminal. Reading needs no account. The CLI publisher uses a NIP-46 remote signer, and GopherKind refuses raw secret keys completely.\n\nTry the idea before reading the explanation:\n\n```\nnpx gopherkind why\nnpx gopherkind read darren@600.wtf\n```\n\nThere is also a [live bridge](https://gopherkind.com/) if installing something to find out what it does feels a bit backwards.\n\nSome old protocols deserve their quiet retirement. Gopher isn't one of them.\n\nGopher got one thing very right in 1991: it separates information from interface. A hole is menus and text. The client decides how those things look, navigation works the same everywhere, and there is very little available for a publisher to use against the reader.\n\nNo cookie banner. No consent platform. No autoplay. No infinite scroll trying to trick me into donating another hour of my life.\n\nCameron Kaiser's essay on why Gopher is still relevant makes the argument properly. GopherKind can read it directly:\n\n```\nnpx gopherkind read gopher://gopher.floodgap.com/0/gopher/relevance.txt\n```\n\nA site has to stand on what it says because there isn't much glitz available to hide behind.\n\nI like that. I don't especially like the structural weakness that comes with it.\n\nIn [RFC 1436](https://www.rfc-editor.org/rfc/rfc1436), a gopherhole is a host. The writing depends on the hobby box under someone's desk, its power supply, a domain registration and the person who still remembers how it was configured.\n\nWhen that box disappears, the hole disappears with it. Copying the files elsewhere can preserve the bytes, but Gopher has no idea who wrote them. There is no authorship in the protocol and nothing in the document that survives the hostname.\n\nNostr has almost exactly the missing pieces. Identity is a public key, events are signed, and an author can put copies on more than one relay. The signature stays with the document regardless of which relay or bridge served it.\n\nThat was too exact a fit to ignore.\n\nGopherKind defines one addressable event kind, `31436`\n\n, numbered after RFC 1436. One event is one text document at one path:\n\n```\n{\n  \"kind\": 31436,\n  \"tags\": [\n    [\"d\", \"/phlog/hello.txt\"],\n    [\"type\", \"0\"],\n    [\"title\", \"Hello from my hole\"]\n  ],\n  \"content\": \"Hello gopherspace.\\n\"\n}\n```\n\nThe `d`\n\ntag is the exact path. Type `0`\n\nis plain text. Type `1`\n\nis a menu written as a `kindmap`\n\n, which is a normal gophermap with the host and port columns removed because the document no longer belongs to a host.\n\n```\nWelcome to my hole\n\n0About me   /about.txt\n1Phlog  /phlog\n1A friend's hole    npub1friend...\nhMy website https://example.com\n```\n\nKind `31436`\n\nis addressable, so publishing a newer event with the same public key and path replaces that page. The link doesn't change and the rest of the hole doesn't need to be rebuilt.\n\nThe complete format is deliberately small. Paths, replacement selection, expiry, deletion, menu parsing and test vectors are in the [proposed specification](https://github.com/forgesworn/gopherkind/blob/main/SPEC.md). The proposal is also open as [nostr-protocol/nips PR #2429](https://github.com/nostr-protocol/nips/pull/2429). It is a proposal, not an accepted NIP, and I won't pretend otherwise.\n\nThis is the part I find most interesting.\n\nA Nostr user doesn't need to publish any kind `31436`\n\nevents before GopherKind can read their hole. When an authored document isn't present, it generates a virtual structure from events the account already has:\n\n```\n/\n/profile.txt\n/notes\n/replies\n/mentions\n/articles\n/follows\n/followers\n/feed.xml\n```\n\nNotes become a phlog. NIP-23 long-form articles become text pages. Available follows and followers become menus, and threads are navigable without reconstructing the conversation from a flat timeline.\n\n```\nnpx gopherkind read npub1...\nnpx gopherkind read npub1.../notes\n```\n\nAn authored kind `31436`\n\npage wins when it uses the same path, so turning the generated hole into your own space is just publishing. There is no migration ceremony and no new identity to create.\n\nNostr clients are generally built around the scroll. A note appears, moves down the timeline and is effectively buried. Gopher has the opposite temperament. A menu stays where the author put it and nothing on the screen is bidding for attention.\n\nThe two protocols cover each other's awkward bits surprisingly well.\n\nAn npub is the durable identity, but it isn't a particularly friendly address to give somebody. GopherKind accepts NIP-05 names anywhere it accepts a hole target, including a path after the name:\n\n```\nnpx gopherkind read darren@600.wtf\nnpx gopherkind read darren@600.wtf/notes\n```\n\n`darren@600.wtf`\n\ncurrently resolves to `npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2`\n\n, then GopherKind reads the signed events for that public key. The [live bridge does the same thing](https://gopherkind.com/go?npub=darren%40600.wtf), redirecting the friendly name to the canonical npub path before it renders the hole.\n\nThat distinction matters. NIP-05 is a human-readable way in, controlled by the domain. It is not the document identity. If the domain owner changes where that name points tomorrow, the existing documents do not silently acquire a new author. Their signatures still belong to the original public key.\n\nA hole on disk is just a directory of text and menu files:\n\n```\nmy-hole/\n|-- index.map\n|-- about.txt\n`-- phlog/\n    |-- index.map\n    `-- 2026-08-10-hello.txt\n```\n\nPublishing goes through a NIP-46 signer you already control. GopherKind never asks for an `nsec`\n\n, never generates one, and refuses a raw secret key if you try to give it one.\n\n```\nnpm install --global gopherkind\n\ngopherkind pair 'bunker://...'\ngopherkind publish ./my-hole --dry-run\ngopherkind publish ./my-hole\ngopherkind inspect npub1...\n```\n\nThe publisher discovers the author's NIP-65 write relays, publishes to their union with the configured relays, and reads every document back. Relay acceptance and successful read-back are reported separately because an `OK`\n\nresponse is not evidence that a reader can retrieve the event later.\n\nWhen the relays can report their current state, editing one page signs one changed document. If they can't be queried, GopherKind republishes everything because not knowing what is there is not the same as knowing it is current. `gopherkind export`\n\nrecovers a relay-held hole into editable files, including a manifest that preserves exact paths which filenames can't safely represent.\n\nThat gives me a useful recovery loop:\n\n```\ngopherkind inspect npub1...\ngopherkind export npub1... ./recovered-hole\ngopherkind publish ./recovered-hole\n```\n\n`inspect`\n\nproves what is retrievable now. It does not prove a relay will keep it next year. That distinction matters.\n\nGopherKind can read the relay-held events directly from the command line, or a bridge can expose them over three frontends:\n\n```\nsigned kind 31436 events on Nostr relays\n        |                 |\n        |                 `--> terminal\n        |\n        `--> bridge --> Gopher\n                   |--> Gemini\n                   `--> HTTP\n```\n\nThe public reference bridge is available at:\n\n`gopher://gopherkind.com/`\n\n`gemini://gopherkind.com/`\n\nThe bridge doesn't own the hole. It is one route to signed events held by relays. Run another compatible bridge and it can serve the same documents, or skip bridges completely and read the relays from the CLI.\n\nThat is the important difference from putting a Gopher frontend in front of another website. There is no origin website hiding behind it.\n\nThis would be very easy to oversell, so here are the boundaries.\n\n**Relays are not archives.** Publishing copies to several relays removes one host as the single point of failure. It doesn't guarantee that any relay will retain an event forever. Keep an export and check retrievability.\n\n**Deletion is a request.** NIP-09 asks relays to remove an event. A relay can ignore it, and another client may already have a copy. NIP-40 expiry tells a compliant bridge not to serve an expired event, but it is not a durable tombstone for older revisions a relay still holds.\n\n**A bridge is still a server.** It can go down. The point is that it isn't the one server carrying the only copy of the writing.\n\n**Gopher is plaintext and unauthenticated.** Network Gopher is permanently read-only in GopherKind. Writing happens through the terminal, HTTP or Gemini with a signer the author controls.\n\n**Kind 31436 is still proposed.** The implementation and conformance vectors exist today. Acceptance into the NIPs repository does not.\n\nThose aren't small-print caveats. They are part of deciding whether this is useful for something you care about.\n\nGopherKind is open source, MIT licensed and still deliberately below `1.0.0`\n\n. It needs Node 24 or newer.\n\nReading costs no identity and changes nothing:\n\n```\nnpx gopherkind why\nnpx gopherkind read darren@600.wtf\nnpx gopherkind read darren@600.wtf/notes\nnpx gopherkind read gopherkind@gopherkind.com\nnpx gopherkind read gopher://gopher.floodgap.com/1/\n```\n\nThe [source, documentation and protocol proposal are on GitHub](https://github.com/forgesworn/gopherkind). The [live bridge](https://gopherkind.com/) is there if you want to poke around first.\n\nI don't expect Gopher to replace the web. I do think some writing deserves a calmer interface and an address that isn't load-bearing on one forgotten box.\n\nGopherKind is still pre-1.0. The bridge works, the npm package works and the NIP proposal remains a proposal. That's the honest state of it.", "url": "https://wpnews.pro/news/i-put-gopher-on-nostr-now-every-npub-is-a-gopherhole", "canonical_source": "https://dev.to/thecryptodonkey/i-put-gopher-on-nostr-now-every-npub-is-a-gopherhole-ahh", "published_at": "2026-08-10 22:46:15+00:00", "updated_at": "2026-08-10 23:15:43.151001+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence"], "entities": ["GopherKind", "Nostr", "Cameron Kaiser", "RFC 1436", "Gopher", "Gemini", "NIP-46"], "alternates": {"html": "https://wpnews.pro/news/i-put-gopher-on-nostr-now-every-npub-is-a-gopherhole", "markdown": "https://wpnews.pro/news/i-put-gopher-on-nostr-now-every-npub-is-a-gopherhole.md", "text": "https://wpnews.pro/news/i-put-gopher-on-nostr-now-every-npub-is-a-gopherhole.txt", "jsonld": "https://wpnews.pro/news/i-put-gopher-on-nostr-now-every-npub-is-a-gopherhole.jsonld"}}