💾 Archived View for gemini.thegonz.net › glog › 200624-RREPLs.gmi captured on 2022-01-08 at 13:40:17. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-11-30)
-=-=-=-=-=-=-
The "Choose Your Own Adventure" approach to interaction in gemini, presenting the user with finitely many options presented as links, is reasonably powerful but limited in scope. We may want richer interaction. On the web, this meant fiddly forms and eventually javascript and ajax and on and on. We don't want to repeat any of that. But we can look further back for inspiration.
The read-eval-print-loop is a venerable and versatile model of interaction. The user enters a line of text, this is interpreted somehow, state is updated as appropriate, and something is printed in response. (Unix) shells may be the best known examples, others include the LISP repl, line-based editors (ed), line-based mail readers (mail/mailx), parser interactive fiction (adventure/zork/photopia), line-based web and gemini clients (CERN LineMode, av98), and so on.
Meanwhile, with gemini query strings, we can send a line of text to a server, have this update some state on the server, and receive something (text, or indeed anything else) back. Iterating this, we have (to coin a phrase) a read-remote-eval-print-loop (RREPL): a line of text is read from the user, it is sent as the query string of a request to a fixed uri, the server's response is displayed, then the next line is read from the user, and so on. Client certificates can be used to keep track of sessions (anonymous or otherwise).
So, we have a well-proven pattern for textual interaction which seems perfectly matched to the affordances of gemini. I see only two hitches.
This is "just" an interface annoyance. Gemini responses can *either* ask for a line of input (10) *or* return a response (20). The closest we can get to combining the two is to have the response include a link to an uri which will return 10, which is pretty clumsy.
Clumsy example of this clumsy technique -- down for now, sorry!
One way to solve this would be to add a new 2x response code meaning "further input accepted" (by making a new request with a new query string).
Alternatively, this can be seen as a problem for clients to solve themselves -- they should provide an easy way to resubmit a request with a changed query, and perhaps to enter a mode in which the client acts as a RREPL at the current uri, interpreting each line typed by the user as a query string.
EDIT: the "repl" command in diohsc now implements this.
This one is harder to deal with. The problem is that gemini is not optimised for repeated requests. Optimally, we'd use a single connection to the server for the whole RREPL interaction. But in gemini, the connection is closed after a single request and response. There are substantial overheads in establishing a new connection for each request.
However, if I understand correctly, the "0-RTT" resumption with early data in TLS1.3 gets us pretty close to that ideal. It does still involve establishing a new TCP connection (SYN-ACK), so requires 2 round trips despite the name, but otherwise the overheads seem pretty minimal. It seems naive implementations are vulnerable to replay attacks and lack forward security, which would be a problem, but as long as each pre-shared key is accepted only once then all appears to be well (IIUC!).
Haskell library for tls resumption which seems to deal with the replay attack problem
EDIT(2020-07-12): after dealing with a bug in the haskell tls library, I got this
working; the example link above will now accept 0-RTT, so with any client
which implements 0-RTT (e.g. diohsc) it should be pretty fast.