-20
readme.md
-20
readme.md
···
1
-
# excuse
2
-
3
-
mount jetstream as a userspace character device on linux.
4
-
5
-
## usage
6
-
7
-
```
8
-
λ cargo b
9
-
λ sudo RUST_LOG=info ./target/debug/excuse
10
-
11
-
# -- in another shell --
12
-
λ cat /dev/jetstream | jq
13
-
```
14
-
15
-
## motivation
16
-
17
-
> People who think that userspace filesystems are realistic
18
-
> for anything but toys are just misguided
19
-
>
20
-
> Linus Torvalds
+42
readme.txt
+42
readme.txt
···
1
+
excuse - mount jetstream as a character device on linux
2
+
3
+
CUSE is FUSE's little brother, and it allows mounting a
4
+
single character device file in /dev, with the device driver
5
+
being implemented in userspace.
6
+
7
+
unlike typical files, character devices like /dev/urandom
8
+
are not seekable. they only support a streaming interface:
9
+
10
+
λ cat /dev/urandom # random numbers
11
+
12
+
excuse simply mounts a websocket connection to an atproto
13
+
jetstream as a userspace character device:
14
+
15
+
λ sudo RUST_LOG=info excuse
16
+
[2025-09-21 INFO excuse] Initializing CUSE at /dev/jetstream
17
+
[2025-09-21 INFO excuse] /dev/jetstream is now 0644
18
+
19
+
in another terminal:
20
+
21
+
λ cat /dev/jetstream | jq
22
+
{
23
+
"did": "did:plc:s6zjj6aw652mmvsrs573j6ti",
24
+
"time_us": 1758442606746247,
25
+
"kind": "commit",
26
+
"commit": { ... }
27
+
}
28
+
.
29
+
.
30
+
.
31
+
32
+
you will notice a couple of optimizations:
33
+
34
+
- the websocket is only opened when the file has readers,
35
+
and is closed when all readers are closed
36
+
- the same connection is shared by all readers
37
+
- the data is buffered and not sent byte by byte
38
+
39
+
it is a bit of a gimmick however, you can do this easily
40
+
with websocat:
41
+
42
+
λ websocat wss://jetstream1.us-east.fire.hose.cam/subscribe
+5
-7
src/main.rs
+5
-7
src/main.rs
···
91
91
break;
92
92
}
93
93
Some(Err(e)) => {
94
-
error!("websocket error: {}", e);
94
+
error!("websocket error: {e}");
95
95
break;
96
96
}
97
97
None => {
···
105
105
}
106
106
}
107
107
Err(e) => {
108
-
error!("failed to connect to websocket: {}", e);
108
+
error!("failed to connect to websocket: {e}");
109
109
}
110
110
}
111
111
···
182
182
reply: fuser::ReplyEmpty,
183
183
) {
184
184
info!(
185
-
"release(ino: {:#x?}, fh: {}, flags: {:#x?}, lock_owner: {:?}, flush: {})",
186
-
ino, fh, flags, lock_owner, flush
185
+
"release(ino: {ino:#x?}, fh: {fh}, flags: {flags:#x?}, lock_owner: {lock_owner:?}, flush: {flush})"
187
186
);
188
187
self.end_stream();
189
188
reply.ok();
···
198
197
let handle = thread::spawn(|| {
199
198
fuser::cuse(device).unwrap_or_else(|e| {
200
199
error!(
201
-
"failed to start cuse device: {}. try run this example as privileged user",
202
-
e
200
+
"failed to start cuse device: {e}. try run this example as privileged user"
203
201
);
204
202
std::process::exit(1);
205
203
})
···
207
205
208
206
// make the device readable without sudo
209
207
let output = Command::new("chmod")
210
-
.args(&["644", &format!("/dev/{DEV}")])
208
+
.args(["644", &format!("/dev/{DEV}")])
211
209
.output();
212
210
213
211
match output {