{"id":13770887,"url":"https://github.com/bhavanki/doppio","last_synced_at":"2025-05-11T03:32:56.111Z","repository":{"id":145725549,"uuid":"317687355","full_name":"bhavanki/doppio","owner":"bhavanki","description":"A Gemini server written in Java.","archived":false,"fork":false,"pushed_at":"2021-06-06T16:05:28.000Z","size":376,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-17T06:40:30.214Z","etag":null,"topics":["gemini","gemini-protocol"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bhavanki.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-12-01T22:43:55.000Z","updated_at":"2023-12-27T07:45:36.000Z","dependencies_parsed_at":"2024-01-06T20:46:24.267Z","dependency_job_id":"648996e9-f65c-48c0-81c6-3d3908633776","html_url":"https://github.com/bhavanki/doppio","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhavanki%2Fdoppio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhavanki%2Fdoppio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhavanki%2Fdoppio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bhavanki%2Fdoppio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bhavanki","download_url":"https://codeload.github.com/bhavanki/doppio/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253514352,"owners_count":21920327,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gemini","gemini-protocol"],"created_at":"2024-08-03T17:00:43.907Z","updated_at":"2025-05-11T03:32:55.786Z","avatar_url":"https://github.com/bhavanki.png","language":"Java","funding_links":[],"categories":["Servers"],"sub_categories":["Graphical"],"readme":"# Doppio\n\n\u003cimg src=\"src/main/resources/doppio.png\" alt=\"Doppio logo\" width=\"200px\"\u003e\n\nA [Gemini](https://gemini.circumlunar.space/) server.\n\n[![Java CI with Maven](https://github.com/bhavanki/doppio/actions/workflows/maven.yml/badge.svg)](https://github.com/bhavanki/doppio/actions/workflows/maven.yml)\n[![CodeQL](https://github.com/bhavanki/doppio/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/bhavanki/doppio/actions/workflows/codeql-analysis.yml)\n\n## Trying It Out\n\n```\n$ docker run --rm -d -p 1965:1965 -e DOPPIO_HOSTNAME=example.com \\\n  bhavanki/doppio:latest\n```\n\nUse a hostname that resolves to the Docker host. Then, point your favorite Gemini client to your host (e.g., gemini://example.com/) to see the default welcome page. Welcome to Geminispace!\n\n## Building\n\nUse [Apache Maven](https://maven.apache.org/).\n\n```\n$ mvn package\n```\n\nThe result is a shaded executable JAR.\n\n## Running\n\n### Certificate Generation\n\nGemini requires a server certificate. You can generate one yourself, or let Doppio generate a temporary, non-persisted certificate that expires after one day.\n\nThis example keytool command generates an elliptic curve (EC) key and corresponding certificate.\n\n```\n$ keytool -genkeypair -keystore doppio.jks -storepass doppio -keyalg EC\n```\n\nAlternatively, use openssl (again, this example generates an EC key).\n\n```\n$ openssl ecparam -genkey -name prime256v1 -out doppio.key\n$ openssl req -new -key doppio.key -x509 -out doppio.crt\n$ openssl pkcs12 -export -inkey doppio.key -in doppio.crt -out doppio.p12\n```\n\nUse a common name that matches your hostname.\n\n### Server Properties File\n\nThen, create a server properties file. Use [doppio-example.yaml](doppio-example.yaml) or [doppio-example.properties](doppio-example.properties) as an example. Set the following properties:\n\n* `host` must match the name on the server certificate.\n* `keystore` must point to the keystore (e.g., JKS or PKCS#12 file) containing the server's private key, if not using a temporary certificate.\n* `keystorePassword` must contain the password for the keystore, if not using a temporary certificate.\n\nWhen using a temporary certificate, do not set `keystore` and `keystorePassword`.\n\nWhen running Doppio in a container, set path server properties such as `keystore` to point to paths in the container, not on the host.\n\n### Running Directly\n\nRun the JAR.\n\n```\n$ java -jar target/doppio-*.jar doppio.yaml\n```\n\n### Running via Docker\n\nRun the Doppio Docker image.\n\n* Mount your server properties file to _/etc/doppio/doppio.yaml_.\n* The image is set up by default to generate a temporary certificate. To use a persistent one, mount it to the path specified by `keystore` in the server properties file.\n\n```\n$ docker run --rm -d -p 1965:1965 \\\n  -e DOPPIO_HOSTNAME=example.com \\\n  --mount type=bind,src=/host/path/to/doppio.yaml,dst=/etc/doppio/doppio.yaml \\\n  --mount type=bind,src=/host/path/to/keystore.jks,dst=/etc/doppio/keystore.jks \\\n  bhavanki/doppio:latest\n```\n\n### Ports\n\nDoppio listens on two ports:\n\n* a server port for Gemini connections: configuration property `port`, default 1965\n* a control port for control commands: configuration property `controlPort`, default 31965\n\nConnections to the control port may only be made from the loopback address. If the `controlPort` configuration property is set to -1, then Doppio does not open a control port.\n\nThe control port is not supported when running Doppio in a container.\n\n### Temporary Certificate Caveat\n\nDoppio uses \"internal proprietary API\" code from the `sun.security` package to generate temporary server certificates. So, this feature might not work on JDKs besides the Oracle JDK and OpenJDK.\n\n## Control Commands\n\nA control command is a single line of text.\n\n* `shutdown`: gracefully shuts down the server\n\nAn easy way to send control commands is with netcat.\n\n```\n$ nc localhost 31965\nshutdown\n```\n\n## Access Log\n\nA log directory may be configured with the `logDir` configuration property. When the property is set, Doppio writes an access log to a file \"access.log\" in that directory. The log follows the [Apache Common Log Format (CLF)](https://httpd.apache.org/docs/1.3/logs.html#common), with the following minor caveats.\n\n* The second field in each line, the RFC 1413 client identity, is never provided.\n* The remote user is the subject DN of the client's authenticated certificate. The value is URL-encoded, primarily to avoid spaces in the logged value.\n\nThe Doppio Docker image establishes _/var/log/doppio_ as a volume for logging.\n\n## Static File Support\n\nPlace static resources in the configured root directory. The Doppio Docker image establishes _/var/gemini_ as a volume for static resources. To bind mount a directory on the Docker host to that location:\n\n```\n... --mount type=bind,src=/host/path/to/root,dst=/var/gemini ...\n```\n\nBy default, resource content is streamed to clients exactly as it is in its resource. To force the conversion of line endings in text resources to canonical form (CRLF or \"\\r\\n\"), set the `forceCanonicalText` server property to `true`.\n\nContent type is detected using Java's built-in mechanism, with additional support for recognizing a configurable set of file suffixes for text/gemini resources (defaults _.gmi_ and _.gemini_).\n\nCharset for text resources is optionally detected using [juniversalchardet](https://github.com/albfernandez/juniversalchardet). By default, charset detection is disabled. Caveats:\n\n* Charset detection is always based on heuristics, so the detector may guess incorrectly sometimes.\n* Detection requires reading the text resource an additional time before it is served, negatively affecting performance.\n\nWhen a directory is requested, Doppio looks for an index file, ending with any supported filename suffix for text/gemini files (e.g., _index.gmi_), and returns the first one it finds. Otherwise, it returns a 51 (not found) response.\n\n## Favicon Support\n\nSet the `favicon` server property to an emoji to configure a favicon. Doppio then serves a favicon document in accordance with the [favicon RFC](gemini://mozz.us/files/rfc_gemini_favicon.gmi). If the property is not set, a literal favicon document may still be created at and served from _/favicon.txt_.\n\n## CGI Support\n\nPlace CGI scripts in the configured CGI directory. If no directory is configured, Doppio does not run scripts, even if they reside in a directory that seems like a valid CGI directory (e.g., some \"cgi-bin\" directory).\n\nDoppio tries to follow [RFC 3875](https://tools.ietf.org/html/rfc3875) in its CGI support. Here are salient limitations and variations from the standard in the implementation.\n\n* UTF-8 is expected for script response headers.\n* The following meta-variables (environment variables) are not supported, since they are not applicable to Gemini: CONTENT_LENGTH, CONTENT_TYPE. The REMOTE_IDENT meta-variable is not implemented. REQUEST_METHOD is set to an empty string.\n* Request bodies are not supported, since Gemini does not support them. The URI query string and extra path information are the only input mechanisms.\n* NPH (Non-Parsed Header) scripts are not supported.\n* Doppio does not check if a client redirect response is well-formed in terms of response headers.\n* Client redirect responses with document are not supported, because Gemini does not permit response bodies in redirects.\n* Status codes 20 and 30 are used as defaults for successful responses and redirects, instead of (HTTP) 200 and 302. The \"bad request\" status code is 59 instead of (HTTP) 400.\n\nLocal redirects *are* supported, up to the maximum per request configured with the `maxLocalRedirects` server property.\n\nThe following TLS-related meta-variables are also supported. Those in the middle column are always set when applicable. Those in the right column, derived from [Apache mod_ssl](https://httpd.apache.org/docs/current/mod/mod_ssl.html), are only set when the `useModSslCgiMetaVars` server configuration property is set to `true`.\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003edefinition\u003c/th\u003e\n    \u003cth\u003eTLS meta-variable\u003c/th\u003e\n    \u003cth\u003eSSL (mod_ssl) meta-variable\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003ecipher name\u003c/td\u003e\n    \u003ctd\u003eTLS_CIPHER\u003c/td\u003e\n    \u003ctd\u003eSSL_CIPHER\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eTLS protocol version\u003c/td\u003e\n    \u003ctd\u003eTLS_VERSION\u003c/td\u003e\n    \u003ctd\u003eSSL_PROTOCOL\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esession ID\u003c/td\u003e\n    \u003ctd\u003eTLS_SESSION_ID\u003c/td\u003e\n    \u003ctd\u003eSSL_SESSION_ID\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eclient certificate serial number\u003c/td\u003e\n    \u003ctd\u003eTLS_CLIENT_SERIAL\u003c/td\u003e\n    \u003ctd\u003eSSL_CLIENT_M_SERIAL\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eclient certificate version\u003c/td\u003e\n    \u003ctd\u003eTLS_CLIENT_VERSION\u003c/td\u003e\n    \u003ctd\u003eSSL_CLIENT_M_VERSION\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eclient certificate fingerprint\u003c/td\u003e\n    \u003ctd\u003eTLS_CLIENT_HASH\u003c/td\u003e\n    \u003ctd\u003e-\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eclient certificate issuer DN\u003c/td\u003e\n    \u003ctd\u003eTLS_CLIENT_ISSUER\u003c/td\u003e\n    \u003ctd\u003eSSL_CLIENT_I_DN\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eclient certificate subject DN\u003c/td\u003e\n    \u003ctd\u003eTLS_CLIENT_SUBJECT\u003c/td\u003e\n    \u003ctd\u003eSSL_CLIENT_S_DN\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eclient certificate validity start timestamp\u003c/td\u003e\n    \u003ctd\u003eTLS_CLIENT_NOT_BEFORE\u003c/td\u003e\n    \u003ctd\u003eSSL_CLIENT_V_START\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eclient certificate validity end timestamp\u003c/td\u003e\n    \u003ctd\u003eTLS_CLIENT_NOT_AFTER\u003c/td\u003e\n    \u003ctd\u003eSSL_CLIENT_V_END\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eclient certificate validity remaining time, in days\u003c/td\u003e\n    \u003ctd\u003eTLS_CLIENT_REMAIN\u003c/td\u003e\n    \u003ctd\u003eSSL_CLIENT_V_REMAIN\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nText output from CGI scripts is subject to line ending conversion if the `forceCanonicalText` server property is set to `true`. Because CGI scripts emit their own response headers, Doppio does not detect content type or charset for them.\n\n## Secure Domains\n\n_Note: This feature replaces \"secure directories\" in earlier versions of Doppio._\n\nA secure domain is a combination of:\n\n* a resource directory\n* an optional truststore\n\nPlace resources (static or CGI) which should require client authentication in the directory of one of the configured secure domains. If a secure domain has a truststore, then Doppio authorizes client certificates against that truststore. So, the truststore may contain individual self-signed certificates, or the root certificate for trusted authorities, or both.\n\nIf a secure domain has no truststore, then Doppio still requires client authentication, but accepts any client certificate.\n\nDoppio validates a client certificate (e.g., checks its valid date range) only when authentication is required for a requested resource.\n\nCGI scripts may roll their own client authentication and authorization code instead of relying on a secure domain. In this case, however, the remote user is not available in the access log for a request, since Doppio is not performing the authentication itself.\n\nTo add a certificate to a new or existing truststore, use the [example script](etc/add-to-truststore.sh) or `keytool` directly:\n\n```\n$ keytool -importcert -file trustedcert.pem -alias trustedcert \\\n  -keystore domaints.jks\n```\n\n## Automatic Atom Feed Generation\n\nDoppio can automatically generate an [Atom feed](https://en.wikipedia.org/wiki/Atom_(Web_standard)) for Gemini index pages that follow the [Subscribing to Gemini pages](gemini://gemini.circumlunar.space/docs/companion/subscription.gmi) specification. Enable this for pages by listing their paths relative to the server root in the `feedPages` configuration property.\n\n```yaml\nfeedPages:\n  - gemlog/index.gmi\n  - gemlog2/index.gmi\n```\n\nWhen Doppio receives a request for an \"atom.xml\" file in a directory that matches a feed page, it returns the generated feed content.\n\n* Requests for the feed page itself still work.\n* Only one feed per directory is supported. If there are multiple feed pages listed for a single directory, the first one listed wins.\n* Automatic feed generation for CGI is not supported.\n* A feed page must use the UTF-8 charset.\n\n## License\n\n[GNU Affero General Public License v3](LICENSE)\n\nThe [example systemd service file](etc/doppio.service) and [example truststore script](etc/add-to-truststore.sh) are separately available under the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbhavanki%2Fdoppio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbhavanki%2Fdoppio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbhavanki%2Fdoppio/lists"}