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
···11-# Fortransky
11+# Fortransky
2233Yes, that Fortran.
4455-A terminal-only Bluesky / AT Protocol client written in Fortran, with a Rust
66-native firehose decoder for the `relay-raw` stream path.
55+A terminal-only Bluesky / AT Protocol client written in Fortran, with a native
66+firehose decoder for the `relay-raw` stream path.
7788Project blog post: https://www.patreon.com/posts/153457794
99-Repo on Tangled soon.
99+1010+---
10111112## Architecture
1213···1718 └─ Rust staticlib (bridge/firehose-bridge/)
1819 envelope → CAR → DAG-CBOR → NormalizedEvent → JSONL
1920 + firehose_bridge_cli binary (used by relay_raw_tail.py)
2121+2222+relay-raw stream path:
2323+ 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
2027```
21282222-Session state is saved in `~/.fortransky/session.json`. Use an app password,
2929+Session state is saved to `~/.fortransky/session.json`. Use an app password,
2330not your main Bluesky password.
24312532---
···44514552The `relay_raw_tail.py` helper is launched as a subprocess by the TUI. It must
4653be able to import `cbor2` and `websockets` using whichever `python3` is on
4747-`PATH` at the time Fortransky runs — not just in an active venv.
5454+`PATH` when Fortransky runs.
48554956**Option A — system-wide (simplest):**
5057```bash
5158sudo pip install cbor2 websockets --break-system-packages
5259```
53605454-**Option B — venv, then symlink or alias:**
6161+**Option B — venv, run with venv active:**
5562```bash
5663python3 -m venv .venv
5764source .venv/bin/activate
5865pip install cbor2 websockets
5966```
6060-Then either run Fortransky with the venv active, or set:
6767+6868+### Assemblersky (optional, relay-raw native decoder)
6969+7070+Assemblersky is an x86-64 assembly AT Protocol firehose decoder.
7171+If the binary is present at `bridge/assemblersky/bin/assemblersky_cli`,
7272+`relay_raw_tail.py` will use it automatically for single-frame decode.
7373+7474+Build from source: https://github.com/FormerLab/assemblersky
7575+6176```bash
6262-export FORTRANSKY_RELAY_PYTHON=$PWD/.venv/bin/python3
7777+cd /path/to/assemblersky
7878+make
7979+mkdir -p /path/to/fortransky/bridge/assemblersky/bin
8080+cp rust-harness/target/release/assemblersky-harness \
8181+ /path/to/fortransky/bridge/assemblersky/bin/assemblersky_cli
6382```
6464-(support for this env var is planned)
8383+8484+Check detection: `./scripts/check_assemblersky.sh`
65856686---
6787···69897090```bash
7191./scripts/build.sh
7272-```
7373-7474-This builds the Rust bridge first (`cargo build --release`), then runs CMake.
7575-The Rust step is skipped on subsequent builds if nothing changed.
7676-7777-```bash
7892./build/fortransky
7993```
8094···82968397## Login
84988585-Use an [app password](https://bsky.app/settings/app-passwords) created for
8686-Fortransky specifically. At the home prompt:
9999+Use an [app password](https://bsky.app/settings/app-passwords). At the home prompt:
8710088101```
89102l
···129142| `/query` | search |
130143| `b` | back to home |
131144132132-### Notifications view
133133-134134-| Command | Action |
135135-|---------|--------|
136136-| `j` / `k` | move selection |
137137-| `n` / `p` | next / previous page |
138138-| `o` | open thread |
139139-| `r` | reply |
140140-| `b` | back |
141141-142145### Stream view
143146144147| Command | Action |
···150153151154## Stream modes
152155153153-**jetstream** — connects to Bluesky's Jetstream WebSocket service. Lower
154154-bandwidth, JSON native, easiest to work with.
156156+**jetstream** — Bluesky's Jetstream WebSocket service. Lower bandwidth, JSON
157157+native, easiest to work with.
155158156156-**relay-raw** — connects to the raw AT Protocol relay
157157-(`com.atproto.sync.subscribeRepos`). Frames are binary CBOR over WebSocket.
158158-The native Rust decoder (`firehose_bridge_cli`) handles envelope → CAR →
159159-DAG-CBOR → normalized JSON. Python cbor2 fallback remains for fixture mode.
159159+**relay-raw** — raw AT Protocol relay (`com.atproto.sync.subscribeRepos`).
160160+Binary CBOR frames over WebSocket, decoded in Python with cbor2. The native
161161+Assemblersky decoder (x86-64 assembly) is used when available for single-frame
162162+decode; the Rust `firehose_bridge_cli` is the fallback.
160163161164### Native decoder detection order
162165163163-1. `FORTRANSKY_FIREHOSE_DECODER` environment variable
164164-2. `bridge/firehose-bridge/target/release/firehose_bridge_cli`
165165-3. `bridge/firehose-bridge/target/debug/firehose_bridge_cli`
166166-4. `firehose_bridge_cli` on `PATH`
166166+1. `FORTRANSKY_RELAY_DECODER` environment variable
167167+2. `FORTRANSKY_ASSEMBLERSKY_DECODER` environment variable
168168+3. `bridge/assemblersky/bin/assemblersky_cli` (bundled Assemblersky)
169169+4. `assemblersky_cli` on `PATH`
170170+5. `FORTRANSKY_FIREHOSE_DECODER` environment variable
171171+6. `bridge/firehose-bridge/target/release/firehose_bridge_cli`
172172+7. `bridge/firehose-bridge/target/debug/firehose_bridge_cli`
173173+8. `firehose_bridge_cli` on `PATH`
167174168168-### Relay fixture (offline testing)
169169-170170-By default relay-raw uses a bundled synthetic fixture. To use the live relay:
171171-172172-```bash
173173-FORTRANSKY_RELAY_FIXTURE=0 ./build/fortransky
174174-```
175175-176176-Quick offline demo:
175175+### Offline fixture testing
177176178177```bash
179178printf 'b\nm\nj\nb\nq\n' | ./build/fortransky
···184183## Known issues / notes
185184186185- JSON parser is hand-rolled and lightweight — not a full schema-driven parser
187187-- `relay-raw` only surfaces `app.bsky.feed.post` create ops; other collections
188188- are filtered out at the normalize stage
189189-- Stream view shows raw DIDs; handle resolution (DID → handle lookup) is not
190190- yet implemented
186186+- `relay-raw` only surfaces `app.bsky.feed.post` create ops
187187+- Stream view shows raw DIDs; handle resolution is done where available
191188- The TUI is line-based (type command + Enter), not raw keypress
192189- `m` and `j` for stream control are home view commands — go `b` back to home
193190 first if you are in the post list
···196193197194## Changelog
198195196196+**v1.2** — Assemblersky integration. `relay_raw_tail.py` detects and prefers
197197+`assemblersky_cli` over the Rust bridge. Live relay-raw decode via Python cbor2
198198+(CBOR tag / CIDv1 handling fixed). Decoder detection order documented.
199199+199200**v1.1** — Native Rust firehose decoder integrated. `relay_raw_tail.py` prefers
200201`firehose_bridge_cli` when found. CMakeLists wires Rust staticlib into the
201201-Fortran link. JWT field lengths bumped to 1024 to fit full AT Protocol tokens.
202202-JSON key scanner depth-tracking fix (was matching nested keys before top-level
203203-`feed` array).
202202+Fortran link. JWT field lengths bumped to 1024. JSON key scanner depth-tracking
203203+fix.
204204205205**v1.0** — Like, repost, quote-post actions. URL facet emission.
206206207207**v0.9** — Typed decode layer (`decode.f90`). Richer post semantics in TUI.
208208209209**v0.7** — C libcurl bridge replacing shell curl. Saved session support.
210210-Stream mode toggle (jetstream / relay-raw).
210210+Stream mode toggle (jetstream / relay-raw).