A Linked Data server in Rust that makes the Semantic Web available over the Small Web (or "smolweb") through a variety of protocols:
More on this in docs/about.md.
- Multi-Protocol Server: Custom Tokio+Rustls implementation handling Gemini and Titan natively over TLS, alongside plaintext Spartan and Nex/NPS TCP listeners. Enabled protocols and ports can be configured.
- Linked Data Store: Consumes RDF data in Turtle via
rio_turtleand holds them into an in-memory store. - Gemtext Mapping: A proposed serialization of RDF to the hypertext format of Gemini, offering a recursively browsable knowledge graph. A condensed syntax, which groups predicates by property, is also supported. The specification is documented at docs/rdf_gemtext_spec.md.
- External Proxy: Acts as a browser for all the Linked Open Data out there.
- Encoded URLs in the request path are fetched via
reqwest. Accept: text/turtleis used for Content Negotiation.- Fetched RDF is parsed and rendered.
- Links to other external resources are re-encoded to point back to the proxy.
- You can provide a custom TLS certificate via the
--certflag.
- Encoded URLs in the request path are fetched via
Pretty standard stuff:
-
Dependencies: Mainly
tokio,rustls,rio_turtle,rio_api, andreqwest. -
Build:
cd server cargo buildOr, if you want to build it as a Docker image:
docker build -t chaykin .(the above will build an image with a self-signed certificate)
-
Run: Either launch the
chaykinexecutable inserver/target, orcargo run
The server listens on
127.0.0.1:1965by default (for Gemini/Titan). Go there with your favourite Gemini client, like Lagrange (simple, stylish, with support for Spartan but not Titan) or Alhena (not as fancy, but flexible and multi-protocol).To run it in a Docker container (mapping the Spartan port to one that doesn't require a super user):
docker run -p 1965:1965 -p 3300:300 -p 1900:1900 -p 1915:1915 chaykin
You can configure the server using command-line arguments:
cargo run -- --helpArguments:
--host: IP address to bind to (default: 127.0.0.1)--gemini-port: Port number for Gemini/Titan to bind to (default: 1965)--spartan-port: Port number for Spartan (default: 300)--nex-port: Port number for Nex (default: 1900)--nps-port: Port number for NPS (default: 1915)--protocols: Comma-separated list of enabled protocols. Runcargo run -- list-protocolsto see the list (default: all)--disable-titan: Disable Titan (assumes Gemini is enabled)--disable-nps: Disable NPS (assumes Nex is enabled)--file: Path to an initial RDF data file (default: sample_data.ttl)--cert: Path to TLS certificate (PEM)--key: Path to TLS private key (PEM)--lang: Preferred language for language-tagged literals in Gemtext (e.g. en, fr, de)
If no certificate/key is provided, a self-signed certificate is generated for development.
The homepage at localhost for each protocol provides helpful hints on where to go from there. To try each protocol straight away:
Fetch a local resource:
printf "gemini://localhost/me\r\n" | openssl s_client -connect 127.0.0.1:1965 -quietProxy an external resource (e.g. Another World on Wikidata), which must be URL-encoded:
printf "gemini://localhost/http%3A%2F%2Fwww.wikidata.org%2Fentity%2FQ257469\r\n" | openssl s_client -connect 127.0.0.1:1965 -quietUpload a URI to inspect via Titan payload:
{ printf "titan://localhost/;size=33;mime=text/plain\r\nhttp://www.wikidata.org/entity/Q257469"; sleep 1; } | openssl s_client -crlf -verify_quiet -connect localhost:1965NOTE: Binding to ports below 1024 (like 300) may require root privileges on Linux/Unix systems.
Connect and pass the target URI as the Spartan payload:
printf "localhost / 35\r\nhttp://www.wikidata.org/entity/Q257469" | nc localhost 300Input the URL-encoded path to get a raw text breakdown:
echo -e "/http%3A%2F%2Fwww.wikidata.org%2Fentity%2FQ257469\n" | nc localhost 1900The following (taken from https://nightfall.city/nps/info/nps) opens a temporary file in your default editor (specified by the $EDITOR environment variable): enter your entity URI and save, and it will be sent to the NPS server.
T=`mktemp` && $EDITOR $T && echo "." >> $T && nc localhost 1915 < $TLots and lots, but mainly:
- Move to RDF support via Sophia and access existing triple stores.
- SPARQL API? Only if it can be implemented without compromising the basic principles of the Small Web.
- Gemtext serialization improvements:
- Support for quads, blank node expansion, RDF-star.
- Context-sensitive links in Gemtext: make them
spartan://orgemini://depending on the client request. - Where possible, add support for language-specific labels per client request (we don't have the luxury of an
Accept-Languageheader here).
- Not every Linked Data server offers Turtle: support negotiation of at least RDF/XML, too.
- Investigate whether it's worth supporting good old Gopher, too.
- Consider Chaykin extensions to existing Small Web servers in Rust, like Agate.
Copyright (c) 2023-2026 Alessandro Adamou.
Chaykin is licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.