π Unseen Servant: Server for Gopher, Gemini, Spartan, Nex, Finger and HTML - Community package now available
-
TL;DR: Unseen Servant publishes one folder of plain text to Gemini, Gopher, Spartan, Nex and Finger at once, and mirrors the same pages to the web. No build step, and nothing executed, ever. Human architected, AI coded. Packaged for Cloudron; unofficial and community-maintained; built and tested on Cloudron 9.x.

What it does- Six surfaces, one content tree: Gemini on 1965 with Titan uploads on the same listener, the web mirror, and, each off until you switch it on, Gopher, Spartan, Nex and Finger.
- Rendering happens when a file changes, not per request. Save a page and it is live everywhere a couple of seconds later, with no second copy to keep in step. The rendered HTML tree is a self-contained static site you can copy anywhere.
- The cleartext protocols cannot leak gated content: anything behind a certificate zone is excluded from the Gopher, Spartan, Nex and Finger trees when they are built, because those protocols cannot authenticate a reader at all.
- Identity that survives the platform: the certificate is minted once per hostname and never silently replaced, through updates, backups, restores and domain moves, so readers who pinned it stay undisturbed.
- Agents are first class: an automated publisher presents a client certificate rather than a password or API key, every read-only command emits JSON, and
/llms.txtwith Markdown siblings serves machine readers. - What it refuses, permanently: CGI, scripting, proxying, plugin APIs, an admin panel, and visitor address logging by default. Content is data, never code.
- The design started by reading the field: Agate's certificate lifecycle, Molly Brown's certificate zones, GmCapsule's Titan handling, gmid's testing discipline and the gopher servers' menu conventions, each credited in the repository with what was declined and why.
Links
Project site: https://unseenservant.dev/
Source and issues: https://forgejo.unseenservant.dev/unseen-servant/unseen-servant- πͺ GitHub mirror: https://github.com/OrcVole/unseen-servant
The site is the demonstration: the page you land on is served by the software, from one folder, on every protocol it supports, and its front page opens with the facts (version, licence, packages, networks, lineage) before it asks you to read a word of prose. Swap
https://forgemini://on the same address and you are reading the same file.
How to installClick the Add custom app dropdown (top right in the App Store) and choose Community app, then paste the CloudronVersions.json URL into the box that pops up. Apps installed this way receive automatic updates.


https://raw.githubusercontent.com/OrcVole/unseen-servant/main/CloudronVersions.jsonOr with the CLI:
cloudron install \ --versions-url https://raw.githubusercontent.com/OrcVole/unseen-servant/main/CloudronVersions.json \ --location capsule.example.comMinimums: 256 MiB RAM,
localstorageonly. No database, no extra subdomain. Cloudron 9.1 or newer.First run: there is no login, which is the point: no admin panel means no credential to leak and no login page to probe. The app writes a starter capsule and serves it immediately; you edit the content folder through the dashboard's File Manager. The Gemini port is fixed at 1965 because every client assumes it; the other four protocols are optional ports in the app's settings, served above 1024 because their canonical ports are privileged.
For usersThe small internet is genuinely nice to write for, and the usual barrier is that publishing there means picking one protocol and one server, then maintaining a second copy for people who only have a browser. Here one folder of plain text is the whole workflow: gemtext in, every surface out, within seconds of saving.
Good fit if you keep a gemlog, or want one, and would rather not run five daemons. Not a fit if you need a general-purpose web server: this one refuses dynamic anything, permanently and by design, and its own site will tell you which of Agate, gmid, Molly Brown or GmCapsule to run instead.
Cloudron specifics worth having: backups include the server identity, so a restore brings back the same certificate your readers pinned; all state is one directory under
/app/data.
On authorshipHuman architected, AI coded
What is offered instead of trust: every design decision recorded in writing before the code, the research published, a wire-level test suite, eight fuzzed parsers, and a conformance run against the live deployment. It is a first release and not independently audited; Agate and gmid have years of production hardening this does not, and the project's own comparison page says so.
π§° For packagers: what we learned
What helped: the app is a single static binary with no database and no supervisor, so the whole package is a Dockerfile, a
start.shand the manifest;localstoragecovered every persistence need.What was tricky: four real ones. The conventional ports for Gopher, Finger and Spartan (70, 79, 300) are privileged and
tcpPortsrefuses them, so the package publishes 1024, 7979 and 3300 instead. Port 3000 is also refused as reserved, which is not in the manifest documentation and only surfaces as a 400 at install time.tcpPortsentries markedreadOnlycannot be moved, which means two installs of this app cannot coexist on one box, correct here but worth knowing. And the health check has to stay green when an admin disables the Gemini port, or the app hangs in "Startingβ¦" because of a protocol-side setting.Still rough: the first install from a versions URL onto a fresh subdomain is the path with the least mileage on it, and confirmations from anyone who tries it are welcome.
οΈ For the Cloudron teamMaintenance burden: light. The package carries no patches, the binary has no runtime dependencies, and the scope is deliberately fixed: new protocols are a versioned decision, not organic growth.
Why it suits the App Store: the niche, the package is small: 256 MiB and a few megabytes on disk.
Friction worth knowing: the community-install validator requires
iconUrl,mediaLinksandpackagerUrl, none of which are in the manifest's own documented field list, and each is discovered as a separate 400 during install.iconUrladditionally forcesminBoxVersionto 9.1.0 or newer, a compatibility floor imposed by a metadata field rather than by anything the app does. A single validation pass listing all missing fields at once would have saved several round trips.
UnlocksA gemlog on your Cloudron that someone in Lagrange, someone in a gopher client from the nineties, and someone who only clicked a link in an email all read from the same files, with no second server and no publishing step.
SynergiesUnseen Servant + Forgejo or Gitea: keep the content in a git repository, pull it into the content folder, and the same tree is a website, a capsule and a gopherhole with no build between them.
Unseen Servant + Miniflux or FreshRSS: it writes Atom, so your own gemlog is subscribable in the reader you already run.
Unseen Servant + Surfer: if you already publish static HTML with Surfer, this adds the small-network surfaces from the same source files rather than replacing anything.
Feedback, bug reports, and "works on our install" confirmations all welcome.
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better π
Register Login