nandi/frqpublic Fork 0
57c71198e1b4399e4d99afa6e0214755c7d33500
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 · 143 lines · 6.5 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
Say when each thing was said dbf3b86 nandi 20d ago20src/frq/clock.jolt the reader's own zone, twelve-hour times, day headings
Pick any emoji, in colour 18c5ccd nandi 20d ago21src/frq/emoji.jolt the picker's catalog: every drawable emoji and its name
Sign in with a Bluesky identity 5192124 nandi 20d ago22src/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 ago23src/frq/state.jolt the ratoms every screen reads, and `apply-msg!`
24src/frq/app.jolt the screens
25```
26
Stop mistaking a quiet connection for a closed one a225fb1 nandi 20d ago27## Tracing
28
29`FRQ_TRACE=1` prints every IRC line sent and received to stderr, which on
30Android is logcat.
31
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago32## Running
33
34`libvidya` from Vidya's Rust/egui backend, then the app:
35
36```bash
37just lib
38just run
39```
40
41`just run` is `jolt -M:frq` with `LD_LIBRARY_PATH` pointed at the built
42library. It connects to `irc.freeq.at:6697` over TLS and joins `#test`. Untick
43TLS on the connect screen (or point it at `127.0.0.1`) for a local server's
44plain listener:
45
46```bash
47cargo run --release --bin freeq-server # in the freeq checkout
48```
49
Sign in with a Bluesky identity 5192124 nandi 20d ago50## Signing in
51
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago52Three modes on the connect screen.
Sign in with a Bluesky identity 5192124 nandi 20d ago53
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago54**Bluesky** (OAuth, the default way in) follows sleek's flow: frq binds a
55loopback port, puts it in `return_to`, and opens
56`auth.freeq.at/auth/login?handle=…`. The broker runs the OAuth dance with the
57PDS and redirects back to that port with the handoff in the URL *fragment*, so
58it never reaches a server as a query string. The page frq serves there has one
59job: POST the fragment back to itself. What comes back is a single-use SASL
60`web-token` and a durable `broker_token`; later connections mint a fresh token
61from the durable one at `/session` and skip the browser.
Sign in with a Bluesky identity 5192124 nandi 20d ago62
Remember an OAuth sign-in across restarts 4aa71e7 nandi 20d ago63The 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 ago64a restart resumes without one, along with the handle and nick it belongs to —
65and it connects on its own at launch when one is there.
Remember an OAuth sign-in across restarts 4aa71e7 nandi 20d ago66The web-token beside it is single-use and deliberately not saved. A token the
67broker no longer honours is dropped — from disk and memory — and the browser
68flow runs once more, rather than failing the same way on every Connect.
69
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago70**App password** signs in without a browser, straight to the user's own PDS:
71`resolveHandle` → DID → PDS from the DID document → `createSession`. The
72password goes to that PDS and nowhere else, is never written to disk, and is
73dropped once the session exists.
Sign in with a Bluesky identity 5192124 nandi 20d ago74
Sign in with Bluesky OAuth, through freeq's broker 2d4a377 nandi 20d ago75Either way freeq sees only a token. The SASL mechanism is
76`ATPROTO-CHALLENGE` in both cases — `method: "web-token"`, which the server
77resolves through its own token store, or `method: "pds-session"` with the
78server's nonce echoed back so the token cannot be replayed elsewhere.
79
80A refused sign-in is reported and the connection carries on as a guest.
Sign in with a Bluesky identity 5192124 nandi 20d ago81
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago82## Android
83
84An APK with two shared libraries and no Java: `libvidya.so` (vidya's Rust/egui
85C ABI, which owns the event loop as the NativeActivity's own library) and
86`libjoltapp.so` (frq compiled to a Chez boot image). Both native halves come
87from the vidya checkout; only the boot image is frq's.
88
89```bash
90./android/build-apk.sh run # build, install, launch on a connected device
91./android/build-apk.sh log # logcat, filtered
92```
93
94Needs what vidya's Android build needs — SDK, NDK r29, and a cross-built Chez
95in `~/.cache/vidya-chez-android`.
96
97TLS does not work there: jolt reaches OpenSSL through the dynamic loader, and
98Android has no public `libssl` to load. The connect screen falls back to the
99plain `:6667` listener on its own, which is why the plain transport is the raw
100`socket`/`connect`/`send`/`recv` calls rather than jolt's `java.net.Socket`
101surface — that surface does not work on Android either, while the syscalls do.
102
103## What the PoC covers
104
105* TLS (`:6697`, via jolt.mvn-http's OpenSSL bindings) or plain TCP (`:6667`)
106* Guest connect (`NICK`/`USER`), `001` welcome, `PING`/`PONG` keepalive
107* Auto-joins `#test` on `irc.freeq.at`
108* Join channels, channel buffers with unread counts, send and receive `PRIVMSG`
Ask for the backlog freeq restores channels without 94e59a2 nandi 20d ago109* Backlog on join, and `CHATHISTORY` for the channels freeq restores instead
Say when each thing was said dbf3b86 nandi 20d ago110* Twelve-hour timestamps from the server's own clock, with a heading wherever
111 the day changes
Answer a message, not just read that one was answered 4876db8 nandi 20d ago112* A chip above a reply quoting what it answers, and a click that goes there;
113 ↩ beside a sender to answer them, with `+draft/reply` on the way out
Pick any emoji, in colour 18c5ccd nandi 20d ago114* Emoji reactions: colour pills under a message, ☺ beside the sender to open a
115 picker over every emoji Vidya can draw (popular first, then Unicode's own
116 groups, searchable by name), and a second click on a pill to take yours off
117 — sent as `TAGMSG`, and restored from the server's own tally when the
118 backlog comes back
Show the pictures people paste 56cdac3 nandi 20d ago119* Inline previews for PNG links, fetched once and cached under
Click a picture to see it full size 563ada3 nandi 20d ago120 `$XDG_CACHE_HOME/frq/media`; click one to see it full size
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago121* Join/part notices, DMs bucketed under the sender's nick
122* Discover list, search over buffers, disconnect
Remember which rooms this client has been in 1d062fd nandi 20d ago123* The rooms you have opened, remembered across runs and listed in the order
124 you last used them (`$XDG_CONFIG_HOME/frq/channels.edn`)
Order the chat list by what you were last in 75e557e nandi 20d ago125* Conversations listed most recently opened first
Show who is talking, with their Bluesky picture f5548bd nandi 20d ago126* Bluesky avatars beside the sender, resolved from the DID freeq tags each
127 message with
A freeq client in jolt, as glimmer components on Vidya 4719d6f nandi 20d ago128
129## Limits
130
131* **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 ago132* **No `did:key` signing, no credential gates, no E2EE.** Sign-in of either
133 kind needs TLS, so it is desktop-only — the Android build connects as a
134 guest.
Remember an OAuth sign-in across restarts 4aa71e7 nandi 20d ago135* **Only the broker token is persisted**, and only for OAuth. An app-password
136 sign-in is not remembered.
Show the pictures people paste 56cdac3 nandi 20d ago137* **Previews are PNG only** — the tree backend's decoder reads no other
138 format, and a fetch needs TLS, so the phone shows links. The link is left in
139 place either way.
140* **Nothing evicts the media cache.**
Show who is talking, with their Bluesky picture f5548bd nandi 20d ago141* **No scrollback trimming, reactions, threads, or calls.**
Stop mistaking a quiet connection for a closed one a225fb1 nandi 20d ago142* 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 ago143* Message lists are keyed vboxes; glimmer-vidya has no `:listbox` yet.