Select the types of activity you want to include in your feed.
A terminal-only Bluesky / AT Protocol client written in Fortran, with a asm/Rust native firehose decoder for the relay-raw stream. DM slide support. Dither image composer. Yes, that Fortran
www.patreon.com/FormerLab
···2233Yes, that Fortran.
4455-A terminal-only Bluesky / AT Protocol client written in Fortran, with a native
66-firehose decoder for the `relay-raw` stream path.
55+A terminal-only Bluesky / AT Protocol client written in Fortran. Posts, timelines,
66+notifications, dithered images, and now DMs — all from an amber terminal, all the
77+way down to the protocol.
7888-Project blog post: https://www.patreon.com/posts/153457794
99+Project blog: https://www.patreon.com/posts/153457794
1010+1111+This is version 1.4, DM sliding...next up is DM E2EE Germ-style and feed composer (true Fortran territory)
1212+Yes, we got a bit of .py and .h and .c, but phasing them out as we go. Not entirely possible
9131014---
1115···1721 └─ Fortran iso_c_binding module (src/atproto/firehose_bridge.f90)
1822 └─ Rust staticlib (bridge/firehose-bridge/)
1923 envelope → CAR → DAG-CBOR → NormalizedEvent → JSONL
2020- + firehose_bridge_cli binary (used by relay_raw_tail.py)
21242225relay-raw stream path:
2326 relay_raw_tail.py
2424- └─ assemblersky_cli (bridge/assemblersky/bin/) ← preferred
2525- └─ firehose_bridge_cli (bridge/firehose-bridge/target/release/) ← fallback
2626- └─ Python cbor2 ← live stream decode
2727+ └─ assemblersky_cli (bridge/assemblersky/bin/) ← x86-64 assembly decoder
2828+ └─ firehose_bridge_cli (bridge/firehose-bridge/target/release/) ← Rust fallback
2929+ └─ Python cbor2 ← live stream decode (CBORTag/CIDv1 aware)
27302831image post path (d <imagepath>):
2929- dither_prep.py image → greyscale flat pixel file
3030- dither.f90 Floyd-Steinberg error diffusion (Bill Atkinson's algorithm)
3131- pixels_to_png.py pixel file → PNG
3232- uploadBlob PNG → Bluesky blob
3333- createRecord post with app.bsky.embed.images
3232+ dither_prep.py image → greyscale flat pixel file
3333+ dither.f90 Floyd-Steinberg error diffusion in Fortran
3434+ pixels_to_png.py pixel file → PNG
3535+ uploadBlob PNG → AT Protocol blob
3636+ createRecord post with app.bsky.embed.images
3737+3838+DM path (dm <handle>, i):
3939+ getConvoForMembers resolve or create conversation
4040+ getMessages fetch message thread
4141+ sendMessage send a DM
4242+ All routed via atproto-proxy header to did:web:api.bsky.chat
4343+ PDS host resolved automatically from plc.directory on login
3444```
35453636-Session state is saved to `~/.fortransky/session.json`. Use an app password,
3737-not your main Bluesky password.
4646+Session state is saved to `~/.fortransky/session.json`. Use an app password
4747+with DM access enabled, not your main Bluesky password.
38483949---
4050···44544555```bash
4656sudo apt install -y gfortran cmake pkg-config libcurl4-openssl-dev
5757+```
5858+5959+### System packages (Arch/Garuda)
6060+6161+```bash
6262+sudo pacman -S gcc-fortran cmake pkgconf curl python-pillow python-cbor2 python-websockets
4763```
48644965### Rust toolchain
···6581Or with a venv (run Fortransky with the venv active):
66826783```bash
6868-python3 -m venv .venv
6969-source .venv/bin/activate
8484+python3 -m venv .venv && source .venv/bin/activate
7085pip install cbor2 websockets Pillow
7186```
72877388### Assemblersky (optional, relay-raw native decoder)
74897575-Assemblersky is an x86-64 assembly AT Protocol firehose decoder.
7676-If the binary is present at `bridge/assemblersky/bin/assemblersky_cli`,
7777-`relay_raw_tail.py` will use it automatically for single-frame decode.
9090+Assemblersky decodes raw AT Protocol firehose frames in x86-64 assembly.
9191+If present at `bridge/assemblersky/bin/assemblersky_cli`, it is preferred
9292+automatically.
78937979-Build from source: https://github.com/FormerLab/assemblersky
9494+9595+## We got a non-public repo for Assemblersky, later when we go public with the Assembler module this is the guide:
9696+9797+Build from source: https://github.com/FormerLab/assemblersky (again, nota public repo right right now)
80988199```bash
8282-cd /path/to/assemblersky
8383-make
100100+cd /path/to/assemblersky && make
84101mkdir -p /path/to/fortransky/bridge/assemblersky/bin
85102cp rust-harness/target/release/assemblersky-harness \
86103 /path/to/fortransky/bridge/assemblersky/bin/assemblersky_cli
···9010791108---
921099393-## Build
110110+## Build and run Fortransky
9411195112```bash
96113./scripts/build.sh
···101118102119## Login
103120104104-Use an [app password](https://bsky.app/settings/app-passwords). At the home prompt:
121121+Use an [app password](https://bsky.app/settings/app-passwords) with DM access
122122+enabled. At the home prompt:
105123106124```
107125l
···110128```
111129112130Session is saved to `~/.fortransky/session.json` and restored on next launch.
131131+Your real PDS host is resolved from `plc.directory` on first login and stored
132132+in the session — required for DMs and other PDS-proxied endpoints.
113133To log out: `x`
114134115135---
···128148| `n` | notifications |
129149| `c` | compose post |
130150| `d <imagepath>` | dither image + post to Bluesky |
151151+| `i` | DM inbox |
152152+| `dm <handle>` | open or start a DM conversation |
131153| `t <uri/url>` | open thread |
132154| `j` | stream tail |
133155| `m` | toggle stream mode (jetstream / relay-raw) |
···148170| `/query` | search |
149171| `b` | back to home |
150172173173+### DM thread view
174174+175175+| Command | Action |
176176+|---------|--------|
177177+| `r` | reply (send message) |
178178+| `j` | refresh messages |
179179+| `b` | back |
180180+151181### Stream view
152182153183| Command | Action |
···160190## Image posting
161191162192The `d` command dithers any image using Bill Atkinson's Floyd-Steinberg
163163-algorithm (as used in MacPaint, 1984) and posts it to Bluesky.
193193+algorithm — the same algorithm used in MacPaint in 1984 — and posts it
194194+to Bluesky as an image embed.
164195165196```
166197d /path/to/image.jpg
···168199169200The image is converted to greyscale, resized to 576×720 (the original MacPaint
170201canvas dimensions), dithered to 1-bit in Fortran, converted to PNG, and posted
171171-with an image embed. Pillow is required.
202202+via `com.atproto.repo.uploadBlob` + `createRecord`. Pillow is required.
203203+204204+---
205205+206206+## DMs
207207+208208+Fortransky implements `chat.bsky.convo.*` — the same DM protocol used by the
209209+official Bluesky app. Requests are proxied through your PDS to
210210+`did:web:api.bsky.chat` via the `atproto-proxy` header.
211211+212212+Your app password must have DM access enabled (the "Allow access to your direct
213213+messages" toggle in Bluesky's app password settings).
214214+215215+PDS host is resolved automatically from `plc.directory` on login and stored in
216216+the session file — no manual configuration needed.
172217173218---
174219···1932387. `bridge/firehose-bridge/target/debug/firehose_bridge_cli`
1942398. `firehose_bridge_cli` on `PATH`
195240196196-### Offline fixture testing
197197-198198-```bash
199199-printf 'b\nm\nj\nb\nq\n' | ./build/fortransky
200200-```
201201-202241---
203242204243## Known issues / notes
205244206206-- JSON parser is hand-rolled and lightweight — not a full schema-driven parser
245245+- JSON parser is hand-rolled — not a full schema-driven parser
207246- `relay-raw` only surfaces `app.bsky.feed.post` create ops
208208-- Stream view shows raw DIDs; handle resolution is done where available
247247+- DM thread view shows raw DIDs; handle resolution coming in a future version
209248- The TUI is line-based (type command + Enter), not raw keypress
210249- `m` and `j` for stream control are home view commands — go `b` back to home
211250 first if you are in the post list
···214253215254## Changelog
216255256256+**v1.4** — DM support via `chat.bsky.convo.*`. `dm <handle>` opens or creates
257257+a conversation, `i` lists the inbox, `r` sends a reply. PDS host auto-resolved
258258+from `plc.directory` on login. `atproto-proxy` header routing to
259259+`did:web:api.bsky.chat`. App password requires DM access enabled.
260260+217261**v1.3** — Floyd-Steinberg dithering + image post via `d <imagepath>`. Bill
218262Atkinson's algorithm (MacPaint, 1984) ported to Fortran. `uploadBlob` +
219219-`createRecord` with image embed wired into the AT Protocol client. Requires
220220-Pillow.
263263+`createRecord` with image embed. Requires Pillow.
221264222265**v1.2** — Assemblersky integration. `relay_raw_tail.py` detects and prefers
223266`assemblersky_cli` over the Rust bridge. Live relay-raw decode via Python cbor2