nandi/frqpublic Fork 0
7ef496a397f9668939daafb44c6187a92cb7ff03
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

README.md · 156 lines · 7.4 KBmarkdown Blame HistoryRaw
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago1# frq
2
3A **[freeq](https://github.com/codegod100/freeq)** client written in
4**[jolt](https://github.com/jolt-lang/jolt)**, as
5[glimmer](https://github.com/jolt-lang/glimmer) components painted by
6**[Vidya](https://tangled.org/nandi.uk/vidya)**/egui.
7
8It is a proof of concept port of [sleek](../sleek), which is the same client in
9Rust against egui directly. The screens are sleek's — connect, chats, chat,
10discover, settings, under a tab bar — but each is hiccup over glimmer's widget
11tags rather than immediate-mode drawing code, and state lives in ratoms instead
12of an `AppState` struct.
13
14```
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago15src/frq/atproto.jolt handle → DID → PDS → session, and the SASL payloads
16src/frq/oauth.jolt the broker flow: login URL, loopback capture, /session
Remember an OAuth sign-in across restarts 4aa71e7 nandi 20d ago17src/frq/store.jolt the saved sign-in, mode 600 in the config directory
Show who is talking, with their Bluesky picture f5548bd nandi 20d ago18src/frq/avatars.jolt profile pictures, by DID or handle
Show the pictures people paste 56cdac3 nandi 20d ago19src/frq/media.jolt image links: spot them, fetch them once, cache on disk
Send a picture by pasting it 8a491ba nandi 19d ago20src/frq/upload.jolt a pasted picture to freeq's media endpoint, as multipart
Say when each thing was said dbf3b86 nandi 20d ago21src/frq/clock.jolt the reader's own zone, twelve-hour times, day headings
Pick any emoji, in colour 18c5ccd nandi 20d ago22src/frq/emoji.jolt the picker's catalog: every drawable emoji and its name
Sign in with a Bluesky identity 5192124 nandi 20d ago23src/frq/irc.jolt IRC over TLS or TCP: parser, reader thread, SASL, PRIVMSG
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago24src/frq/state.jolt the ratoms every screen reads, and `apply-msg!`
25src/frq/app.jolt the screens
26```
27
Stop mistaking a quiet connection for a closed one a225fb1 nandi 20d ago28## Tracing
29
30`FRQ_TRACE=1` prints every IRC line sent and received to stderr, which on
31Android is logcat.
32
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago33## Running
34
35`libvidya` from Vidya's Rust/egui backend, then the app:
36
37```bash
38just lib
39just run
40```
41
42`just run` is `jolt -M:frq` with `LD_LIBRARY_PATH` pointed at the built
43library. It connects to `irc.freeq.at:6697` over TLS and joins `#test`. Untick
44TLS on the connect screen (or point it at `127.0.0.1`) for a local server's
45plain listener:
46
47```bash
48cargo run --release --bin freeq-server # in the freeq checkout
49```
50
Sign in with a Bluesky identity 5192124 nandi 20d ago51## Signing in
52
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago53Three modes on the connect screen.
Sign in with a Bluesky identity 5192124 nandi 20d ago54
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago55**Bluesky** (OAuth, the default way in) follows sleek's flow: frq binds a
56loopback port, puts it in `return_to`, and opens
57`auth.freeq.at/auth/login?handle=…`. The broker runs the OAuth dance with the
58PDS and redirects back to that port with the handoff in the URL *fragment*, so
59it never reaches a server as a query string. The page frq serves there has one
60job: POST the fragment back to itself. What comes back is a single-use SASL
61`web-token` and a durable `broker_token`; later connections mint a fresh token
62from the durable one at `/session` and skip the browser.
Sign in with a Bluesky identity 5192124 nandi 20d ago63
Remember an OAuth sign-in across restarts 4aa71e7 nandi 20d ago64The durable token is saved to `$XDG_CONFIG_HOME/frq/session.edn` (mode 600) so
Connect on launch when an account is remembered 9237cf4 nandi 20d ago65a restart resumes without one, along with the handle and nick it belongs to —
66and it connects on its own at launch when one is there.
Remember an OAuth sign-in across restarts 4aa71e7 nandi 20d ago67The web-token beside it is single-use and deliberately not saved. A token the
68broker no longer honours is dropped — from disk and memory — and the browser
69flow runs once more, rather than failing the same way on every Connect.
70
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago71**App password** signs in without a browser, straight to the user's own PDS:
72`resolveHandle` → DID → PDS from the DID document → `createSession`. The
73password goes to that PDS and nowhere else, is never written to disk, and is
74dropped once the session exists.
Sign in with a Bluesky identity 5192124 nandi 20d ago75
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago76Either way freeq sees only a token. The SASL mechanism is
77`ATPROTO-CHALLENGE` in both cases — `method: "web-token"`, which the server
78resolves through its own token store, or `method: "pds-session"` with the
79server's nonce echoed back so the token cannot be replayed elsewhere.
80
81A refused sign-in is reported and the connection carries on as a guest.
Sign in with a Bluesky identity 5192124 nandi 20d ago82
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago83## Android
84
85An APK with two shared libraries and no Java: `libvidya.so` (vidya's Rust/egui
86C ABI, which owns the event loop as the NativeActivity's own library) and
87`libjoltapp.so` (frq compiled to a Chez boot image). Both native halves come
88from the vidya checkout; only the boot image is frq's.
89
90```bash
91./android/build-apk.sh run # build, install, launch on a connected device
92./android/build-apk.sh log # logcat, filtered
93```
94
95Needs what vidya's Android build needs — SDK, NDK r29, and a cross-built Chez
96in `~/.cache/vidya-chez-android`.
97
98TLS does not work there: jolt reaches OpenSSL through the dynamic loader, and
99Android has no public `libssl` to load. The connect screen falls back to the
100plain `:6667` listener on its own, which is why the plain transport is the raw
101`socket`/`connect`/`send`/`recv` calls rather than jolt's `java.net.Socket`
102surface — that surface does not work on Android either, while the syscalls do.
103
104## What the PoC covers
105
106* TLS (`:6697`, via jolt.mvn-http's OpenSSL bindings) or plain TCP (`:6667`)
107* Guest connect (`NICK`/`USER`), `001` welcome, `PING`/`PONG` keepalive
108* Auto-joins `#test` on `irc.freeq.at`
109* Join channels, channel buffers with unread counts, send and receive `PRIVMSG`
Ask for the backlog freeq restores channels without 94e59a2 nandi 20d ago110* Backlog on join, and `CHATHISTORY` for the channels freeq restores instead
Say when each thing was said dbf3b86 nandi 20d ago111* Twelve-hour timestamps from the server's own clock, with a heading wherever
112 the day changes
Answer a message, not just read that one was answered 4876db8 nandi 20d ago113* A chip above a reply quoting what it answers, and a click that goes there;
114 ↩ beside a sender to answer them, with `+draft/reply` on the way out
Pick any emoji, in colour 18c5ccd nandi 20d ago115* Emoji reactions: colour pills under a message, ☺ beside the sender to open a
116 picker over every emoji Vidya can draw (popular first, then Unicode's own
117 groups, searchable by name), and a second click on a pill to take yours off
118 — sent as `TAGMSG`, and restored from the server's own tally when the
119 backlog comes back
Show the pictures people paste 56cdac3 nandi 20d ago120* Inline previews for PNG links, fetched once and cached under
Click a picture to see it full size 563ada3 nandi 20d ago121 `$XDG_CACHE_HOME/frq/media`; click one to see it full size
Send a picture by pasting it 8a491ba nandi 19d ago122* Ctrl+V in the draft attaches the picture on the clipboard: it is previewed
123 under the box and uploaded to freeq's media endpoint while you write the line
124 it goes with, and only on the way out does it become the link — which is the
125 whole of what sending an image over IRC means. The draft itself is never
126 written into. Text pastes as text, as it always did: the picture path is the
127 keystroke the field had no text to answer with
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago128* Join/part notices, DMs bucketed under the sender's nick
129* Discover list, search over buffers, disconnect
Remember which rooms this client has been in 1d062fd nandi 20d ago130* The rooms you have opened, remembered across runs and listed in the order
131 you last used them (`$XDG_CONFIG_HOME/frq/channels.edn`)
Order the chat list by what you were last in 75e557e nandi 20d ago132* Conversations listed most recently opened first
Show who is talking, with their Bluesky picture f5548bd nandi 20d ago133* Bluesky avatars beside the sender, resolved from the DID freeq tags each
134 message with
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago135
136## Limits
137
138* **TLS and plain TCP only** — no WebSocket, no iroh. On Android, plain only.
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago139* **No `did:key` signing, no credential gates, no E2EE.** Sign-in of either
140 kind needs TLS, so it is desktop-only — the Android build connects as a
141 guest.
Remember an OAuth sign-in across restarts 4aa71e7 nandi 20d ago142* **Only the broker token is persisted**, and only for OAuth. An app-password
143 sign-in is not remembered.
Show the pictures people paste 56cdac3 nandi 20d ago144* **Previews are PNG only** — the tree backend's decoder reads no other
145 format, and a fetch needs TLS, so the phone shows links. The link is left in
146 place either way.
147* **Nothing evicts the media cache.**
Send a picture by pasting it 8a491ba nandi 19d ago148* **Pasting a picture needs a sign-in and a desktop.** The upload is filed
149 under the DID of a live session, so a guest cannot make one; and it is read
150 off the clipboard through the ABI's `vidya_clipboard_image_png`, which
151 arboard backs on desktop and nothing backs on Android. It also shares
152 nothing to your PDS and posts nothing to Bluesky — those fields are opt-in
153 and this client does not send them.
Show who is talking, with their Bluesky picture f5548bd nandi 20d ago154* **No scrollback trimming, reactions, threads, or calls.**
Stop mistaking a quiet connection for a closed one a225fb1 nandi 20d ago155* A sent line waits up to 200ms for the reader thread to flush it.
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago156* Message lists are keyed vboxes; glimmer-vidya has no `:listbox` yet.