๐Ÿ’พ Archived View for bbs.geminispace.org โ€บ s โ€บ Gemini โ€บ 19515 captured on 2024-12-17 at 15:04:57. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

geminiprotocol.net: Gemini app developer's guide published

Thank you @Solderpunk for giving me the chance to contribute some official Gemini documentation in the form of a new developer guide.

I look forward to hearing from @Acidus, @mbays, @gritty, @stack and all other Geminauts who have been working on interactive Gemini apps! I'm sure we can keep improving this guide further with your insights and/or comments and corrections.

Posted in: s/Gemini

๐Ÿ•น๏ธ skyjake [mod, sysop]

Sep 08 ยท 3 months ago ยท ๐Ÿ‘ gritty, jsreed5, freezr, klemperer, zinricky, gemalaya, ian, LucasMW, zeerooth, arma

9 Comments โ†“

๐Ÿฆ‚ zzo38 ยท Sep 09 at 04:49:

This document is good, but I have some comments about it.

2: Another consideration is that I think accessing the same URL multiple times without others in between should ideally not have extra effects. A single URL should not toggle a setting; if a setting menu includes toggle settings, then the link in the setting menu should include what state it is going to be changed to, and the program that produces that menu will make the link set it to the opposite than the setting that it was when the menu was generated. This can avoid some kinds of confusions with the UI depending on how the user navigates it.

3.2: You can support multiple supplementary protocols if desired, I suppose. Furthermore, if the client certificate can easily be accessed from outside of the browser, then it is also possible to use an external program (such as my own "astroget" program, but there are probably others as well) to use Titan. Of course, a user might not have any of this stuff, in which case you would compose longer content in pieces, but that would be difficult to use and not very good, so providing an alternative to that would be a good idea. Telnet/SSH is another possible alternative, although since it is not TLS, it won't use the same certificates, I think (and neither Telnet nor SSH supports virtual hosting, unfortunately).

4.1 and 4.2: Services that use client certificates should also include documentation about what they expect the client certificates to contain (and if it doesn't care, it should mention this, too, to avoid confusion). Like you say, it is a good idea to make most or all of them optional (with the exception of the public key which will be required and perhaps the expiry date), although they can still be helpful if present, and it may be helpful if the user knows which fields are expected, whether they are mandatory or optional. Note that you can define extensions if necessary. (To make up the ID of the extension, use "uuid -FSIV" and prepend "2.25." and optionally append "." and further numbers; and that will be the ID of the extension.)

It may be helpful to be able to somehow securely indicate that one certificate supersedes another, using data within the certificate itself. I don't know if there is a good way to do this that will work for self-signed certificates and will work even if the name needs to be changed or if the key needs to be revoked; although you might need to sign the new key with the old one, as a X.509 extension, perhaps.

5.1: Another option would be a hash of the combination of the certificate and a secret salt (and possibly the current date). The user might deliberately publish their X.509 certificate (or send it to the wrong server by accident; either way, others can't use it for authentication since they don't have the private key), in which case using the hash of the certificate alone can be insecure, so a secret salt will help too.

6.1: Another alterative is sockets. If the requested file refers to a UNIX socket, connect to it and send the information in the header, and read the response from that socket. I implemented this in scorpiond (which implements Scorpion and does not implement Gemini, but a similar mechanism can be used with Gemini (and other protocols, e.g. Spartan) too). I did this originally to avoid the issues with CGI in user home directories, with the user account that the program runs under; the server runs in one account and cannot easily switch to the user's account securely unless it is running as root which is undesriable for other reasons. However, it has other benefits as well, e.g. if you want to keep state in memory for multiple users accessing at once. The format of the header is: Four strings each preceded by a big-endian 32-bit length: the request up to and including the file name, the part of the request after the file name, the client's IP address (as ASCII text), and the client certificate (empty (but still present) if there is no client certificate).

๐Ÿ˜ˆ dimkr ยท Sep 09 at 09:49:

Something I would add to this guide: response streaming. Many "apps" can send the beginning of the page while fetching data that appears later (static title vs. post content from DB), instead of sending the entire page in one chunk, improving user experience for users with high latency. In addition, some clients (including Lagrange) re-render the page as more data is received and may show artifacts or change the page layout if streaming is not line based (for example, display the URL until the link title is received).

๐Ÿš€ LucasMW ยท Sep 09 at 11:46:

Just when I was looking for something like this!

Amazing.

It will be a worth read

๐Ÿš€ mbays ยท Sep 09 at 20:32:

This is very nice! I can see it being very helpful for people starting to play with these things.

I do have a few little suggestions.

I'd mention that X.509 certificates can also have "no well-defined expiration date", indicated by notAfter being set to the maximal value 99991231235959Z (per RFC 5280).

Using the Issuer/Subject fields is mentioned in a few places. I'd prefer to discourage this. Providing the facility to set the various fields when generating a client certificate leads to an ungainly client UI, and not being able to change the information without regenerating the certificate limits its usefulness. The section "About certificate fields" is appropriately discouraging, but the "Implicitly created accounts" item reads like a recommendation of the approach.

The suggestions for linking certificates using passwords seem more complicated than necessary. Here's what I'd recommend:

=> /my-app/linkcert?USER:TOKEN Follow this link with a new certificate to link it to your account

where USER is an identifier of the user's account understood by the application, and TOKEN is a secure hash of USER, a secret salt held only by the server, and the current time rounded to the nearest 300 (say) seconds.

โ€” Implementation of this scheme on my own capsule

"Also, the user may also change the state manually by editing the URL": This can be prevented by appending a random number and a secure hash of all parameters with a secret salt.

A similar technique lets you generate tokens to defeat CSRF which don't need to be stored on the server (re "As a downside, links with tokens will eventually expire, depending on how many old values you are comfortable with keeping stored.").

The mailto: example URI appears to be invalid according to STD-66, because it contains the reserved character '>' unescaped.

Perhaps you want to avoid it until the spec clarifies the issue, but it could be helpful to say something about use of ANSI escapes in Gemini applications. I would say: use only if the user has specifically asked for it, and then only in preformatted blocks.

I would also like a section on the uses and pitfalls of indefinite streaming. There's quite a bit to say about that, the most important being that clients don't have to support rendering of partially received response bodies, and many don't.

๐Ÿฆ‚ zzo38 ยท Sep 10 at 00:04:

I think that it should be OK to use any fields in the client certificates as long as you do not rely on them, and that you should document what fields are used. If accounts are independent from certificates, the account settings can be changed without needing to change the certificate and without requiring any fields in the certificate, although if they are present, they could be used to load the initial values of some settings.

There might be other requirements of certificates as well, such as which types of keys and signatures can be used. If the server sends a TLS certificate request message (which is not the same as the Gemini 6x status code, although it will send that too) to the client, then I would think that the client can know what key types and signature algorithms are acceptable. Therefore, I would think it would probably not be necessary to document these requirements, since TLS will already do this (unless the TLS certificate request message is omitted); however, I am not sure how to check this with OpenSSL. I am not entirely sure of how TLS is working anyways (although I did read the documentation for TLS), so hopefully someone who understands it better might be able to explain if this will work and if so, how.

The security token hash including the current time is something that I had mentioned too (although I mentioned using one day as the interval, although maybe that is too long and five minutes will be better, like mbays suggests). You can check the hash for the previous interval as well as the current interval, in order that they will not expire in only a few seconds.

Using ANSI escapes "only if the user has specifically asked for it" (as mbays suggests) is a reasonable idea and could be an option in a menu, but you could also define a X.509 extension for preloading the setting automatically (but like I mentioned above, you do not need to rely on this).

Indefinite streaming will not be implemented in all clients (it is also possible that some clients will support indefinite streaming but will only render full lines, although as dimkr mentions some might display partial lines even if it results in an incorrect display), so services should avoid relying on that when possible. A service might still send a part of the text and in some cases there might be a necessary delay for the rest of the text (suggested by dimkr), even if a client only displays it when the entire file has been downloaded.

๐Ÿš€ jsreed5 ยท Sep 10 at 01:32:

Excellent writeup! One thing I learned about CGIs is that user state is very important, and one should consider carefully how to handle it. My chess service used a full database to retain player state across requests. Meanwhile, my todo.txt service manages it with only a user certificate and the contents of flat text files. Sometimes one can get away with storing a state within a query string if the user is not expected to modify the query manually; some examples of this are Minesweeper by @Acidus or my twisty puzzles, where users alter the state by selecting links that contain query strings modified according to the rules of the puzzle.

โ„ freezr ยท Sep 13 at 22:54:

Thank you guys you're the best! ๐Ÿ‘

๐Ÿ•น๏ธ skyjake [OP/mod...] ยท Sep 15 at 18:07:

Thanks for the feedback so far, everyone! When I find a suitable moment I'll make a few edits and additions to the guide. This will probably be after the Lagrange v1.18 release, though.

๐Ÿš€ jsreed5 ยท Oct 05 at 14:18:

Here's a neat trick for CGIs I forgot to mention. Sometimes you may want to send a value as a query string but only have that value act on a certain location. For example, you may want to edit just the Nth line of a file, or return the Nth page of search results. One way to do this is to build a base CGI file, then create a symlink to the file, with the link name being the location you want to act on. The CGI can then use its own filename as a location parameter, without the user needing to specify a location in the query along with a value. I use this trick in both my todo.txt service and the OEIS mirror.