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
rust atproto fun fortran assembly
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

README: update to v1.2, Assemblersky integration, decoder detection order

FormerLab c06ed2ca 27806a23

+55 -55
+55 -55
README.md
··· 1 - # Fortransky 1 + # Fortransky 2 2 3 3 Yes, that Fortran. 4 4 5 - A terminal-only Bluesky / AT Protocol client written in Fortran, with a Rust 6 - native firehose decoder for the `relay-raw` stream path. 5 + A terminal-only Bluesky / AT Protocol client written in Fortran, with a native 6 + firehose decoder for the `relay-raw` stream path. 7 7 8 8 Project blog post: https://www.patreon.com/posts/153457794 9 - Repo on Tangled soon. 9 + 10 + --- 10 11 11 12 ## Architecture 12 13 ··· 17 18 └─ Rust staticlib (bridge/firehose-bridge/) 18 19 envelope → CAR → DAG-CBOR → NormalizedEvent → JSONL 19 20 + firehose_bridge_cli binary (used by relay_raw_tail.py) 21 + 22 + relay-raw stream path: 23 + relay_raw_tail.py 24 + └─ assemblersky_cli (bridge/assemblersky/bin/) ← preferred 25 + └─ firehose_bridge_cli (bridge/firehose-bridge/target/release/) ← fallback 26 + └─ Python cbor2 ← live stream decode 20 27 ``` 21 28 22 - Session state is saved in `~/.fortransky/session.json`. Use an app password, 29 + Session state is saved to `~/.fortransky/session.json`. Use an app password, 23 30 not your main Bluesky password. 24 31 25 32 --- ··· 44 51 45 52 The `relay_raw_tail.py` helper is launched as a subprocess by the TUI. It must 46 53 be able to import `cbor2` and `websockets` using whichever `python3` is on 47 - `PATH` at the time Fortransky runs — not just in an active venv. 54 + `PATH` when Fortransky runs. 48 55 49 56 **Option A — system-wide (simplest):** 50 57 ```bash 51 58 sudo pip install cbor2 websockets --break-system-packages 52 59 ``` 53 60 54 - **Option B — venv, then symlink or alias:** 61 + **Option B — venv, run with venv active:** 55 62 ```bash 56 63 python3 -m venv .venv 57 64 source .venv/bin/activate 58 65 pip install cbor2 websockets 59 66 ``` 60 - Then either run Fortransky with the venv active, or set: 67 + 68 + ### Assemblersky (optional, relay-raw native decoder) 69 + 70 + Assemblersky is an x86-64 assembly AT Protocol firehose decoder. 71 + If the binary is present at `bridge/assemblersky/bin/assemblersky_cli`, 72 + `relay_raw_tail.py` will use it automatically for single-frame decode. 73 + 74 + Build from source: https://github.com/FormerLab/assemblersky 75 + 61 76 ```bash 62 - export FORTRANSKY_RELAY_PYTHON=$PWD/.venv/bin/python3 77 + cd /path/to/assemblersky 78 + make 79 + mkdir -p /path/to/fortransky/bridge/assemblersky/bin 80 + cp rust-harness/target/release/assemblersky-harness \ 81 + /path/to/fortransky/bridge/assemblersky/bin/assemblersky_cli 63 82 ``` 64 - (support for this env var is planned) 83 + 84 + Check detection: `./scripts/check_assemblersky.sh` 65 85 66 86 --- 67 87 ··· 69 89 70 90 ```bash 71 91 ./scripts/build.sh 72 - ``` 73 - 74 - This builds the Rust bridge first (`cargo build --release`), then runs CMake. 75 - The Rust step is skipped on subsequent builds if nothing changed. 76 - 77 - ```bash 78 92 ./build/fortransky 79 93 ``` 80 94 ··· 82 96 83 97 ## Login 84 98 85 - Use an [app password](https://bsky.app/settings/app-passwords) created for 86 - Fortransky specifically. At the home prompt: 99 + Use an [app password](https://bsky.app/settings/app-passwords). At the home prompt: 87 100 88 101 ``` 89 102 l ··· 129 142 | `/query` | search | 130 143 | `b` | back to home | 131 144 132 - ### Notifications view 133 - 134 - | Command | Action | 135 - |---------|--------| 136 - | `j` / `k` | move selection | 137 - | `n` / `p` | next / previous page | 138 - | `o` | open thread | 139 - | `r` | reply | 140 - | `b` | back | 141 - 142 145 ### Stream view 143 146 144 147 | Command | Action | ··· 150 153 151 154 ## Stream modes 152 155 153 - **jetstream** — connects to Bluesky's Jetstream WebSocket service. Lower 154 - bandwidth, JSON native, easiest to work with. 156 + **jetstream** — Bluesky's Jetstream WebSocket service. Lower bandwidth, JSON 157 + native, easiest to work with. 155 158 156 - **relay-raw** — connects to the raw AT Protocol relay 157 - (`com.atproto.sync.subscribeRepos`). Frames are binary CBOR over WebSocket. 158 - The native Rust decoder (`firehose_bridge_cli`) handles envelope → CAR → 159 - DAG-CBOR → normalized JSON. Python cbor2 fallback remains for fixture mode. 159 + **relay-raw** — raw AT Protocol relay (`com.atproto.sync.subscribeRepos`). 160 + Binary CBOR frames over WebSocket, decoded in Python with cbor2. The native 161 + Assemblersky decoder (x86-64 assembly) is used when available for single-frame 162 + decode; the Rust `firehose_bridge_cli` is the fallback. 160 163 161 164 ### Native decoder detection order 162 165 163 - 1. `FORTRANSKY_FIREHOSE_DECODER` environment variable 164 - 2. `bridge/firehose-bridge/target/release/firehose_bridge_cli` 165 - 3. `bridge/firehose-bridge/target/debug/firehose_bridge_cli` 166 - 4. `firehose_bridge_cli` on `PATH` 166 + 1. `FORTRANSKY_RELAY_DECODER` environment variable 167 + 2. `FORTRANSKY_ASSEMBLERSKY_DECODER` environment variable 168 + 3. `bridge/assemblersky/bin/assemblersky_cli` (bundled Assemblersky) 169 + 4. `assemblersky_cli` on `PATH` 170 + 5. `FORTRANSKY_FIREHOSE_DECODER` environment variable 171 + 6. `bridge/firehose-bridge/target/release/firehose_bridge_cli` 172 + 7. `bridge/firehose-bridge/target/debug/firehose_bridge_cli` 173 + 8. `firehose_bridge_cli` on `PATH` 167 174 168 - ### Relay fixture (offline testing) 169 - 170 - By default relay-raw uses a bundled synthetic fixture. To use the live relay: 171 - 172 - ```bash 173 - FORTRANSKY_RELAY_FIXTURE=0 ./build/fortransky 174 - ``` 175 - 176 - Quick offline demo: 175 + ### Offline fixture testing 177 176 178 177 ```bash 179 178 printf 'b\nm\nj\nb\nq\n' | ./build/fortransky ··· 184 183 ## Known issues / notes 185 184 186 185 - JSON parser is hand-rolled and lightweight — not a full schema-driven parser 187 - - `relay-raw` only surfaces `app.bsky.feed.post` create ops; other collections 188 - are filtered out at the normalize stage 189 - - Stream view shows raw DIDs; handle resolution (DID → handle lookup) is not 190 - yet implemented 186 + - `relay-raw` only surfaces `app.bsky.feed.post` create ops 187 + - Stream view shows raw DIDs; handle resolution is done where available 191 188 - The TUI is line-based (type command + Enter), not raw keypress 192 189 - `m` and `j` for stream control are home view commands — go `b` back to home 193 190 first if you are in the post list ··· 196 193 197 194 ## Changelog 198 195 196 + **v1.2** — Assemblersky integration. `relay_raw_tail.py` detects and prefers 197 + `assemblersky_cli` over the Rust bridge. Live relay-raw decode via Python cbor2 198 + (CBOR tag / CIDv1 handling fixed). Decoder detection order documented. 199 + 199 200 **v1.1** — Native Rust firehose decoder integrated. `relay_raw_tail.py` prefers 200 201 `firehose_bridge_cli` when found. CMakeLists wires Rust staticlib into the 201 - Fortran link. JWT field lengths bumped to 1024 to fit full AT Protocol tokens. 202 - JSON key scanner depth-tracking fix (was matching nested keys before top-level 203 - `feed` array). 202 + Fortran link. JWT field lengths bumped to 1024. JSON key scanner depth-tracking 203 + fix. 204 204 205 205 **v1.0** — Like, repost, quote-post actions. URL facet emission. 206 206 207 207 **v0.9** — Typed decode layer (`decode.f90`). Richer post semantics in TUI. 208 208 209 209 **v0.7** — C libcurl bridge replacing shell curl. Saved session support. 210 - Stream mode toggle (jetstream / relay-raw). 210 + Stream mode toggle (jetstream / relay-raw).