1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257 |
# Skull Browser
A keyboard-driven web browser that treats the small web as a first-class
citizen. Regular sites over `https://`, plus native `gopher://` and
`gemini://` support. Everything above the C core is Lua, and you can change
all of it.



## What it is
Modal navigation in the vim tradition: `hjkl` to scroll, `f` to follow links by
keyboard hint, `:` for commands. The rendering engine is WebKitGTK, the widget
layer is GTK 3, and roughly sixty Lua modules sit on top providing tabs, modes,
bindings, ad blocking, bookmarks, history, downloads and the internal
`skull://` pages.
Version 0.01. Linux only. It has never been built on macOS, BSD or Windows, so
it is not claimed to work there. On Windows it runs under WSL with WSLg, because
that is Linux.
## Requirements
GTK 3
WebKitGTK 4.1
Lua 5.1 or LuaJIT
lfs (lua filesystem)
socket (lua socket, for gopher and gemini)
ssl (luasec, for gemini TLS)
sqlite3
gstreamer (video playback)
## Install
Clone, pull in the dependencies, build:
git clone https://github.com/runawaydevil/skull-browser.git
cd skull-browser
sh build-utils/setup-deps.sh
make
`setup-deps.sh` handles apt, dnf and pacman. It needs sudo to install and it
verifies everything afterwards, so run it once and read what it prints. To
check an existing system without installing anything:
sh build-utils/setup-deps.sh --check
Then install system-wide:
sudo make install
Or into your home directory, no root needed:
make PREFIX=$HOME/.local XDGPREFIX=$HOME/.local/etc/xdg install
Make sure `$HOME/.local/bin` is on your `PATH` if you go that route.
## Run
skull
skull gopher://gopher.floodgap.com
skull -k # check config and exit
Under WSL, calling it from Windows needs a login shell, otherwise `~/.local/bin`
is not on `PATH` and the name does not resolve:
wsl -d Ubuntu -- bash -lc 'skull gopher://gopher.floodgap.com'
Press `gA` or type `:about` for version information.
## Configuration
User config lives in `~/.config/skull/`. Copy the shipped `rc.lua` there and
edit it:
mkdir -p ~/.config/skull
cp config/rc.lua config/theme.lua ~/.config/skull/
A system install also drops a copy in `/usr/local/etc/xdg/skull/`, which is
picked up automatically when there is nothing in `~/.config/skull/`. A home
install (`PREFIX=$HOME/.local`) puts it somewhere the browser does not search,
so copy it by hand as shown above.
Themes are a flat Lua table in `theme.lua` with a cascading naming scheme:
`tab_selected_fg` falls back to `selected_fg`, then to `fg`. Change a handful
of root keys and the whole interface follows. The internal `skull://` pages
read their colours from the same file through CSS custom properties, so one
edit moves the whole browser.
## Small web
`gopher://` and `gemini://` are rendered by the browser itself, not proxied.
Both share one presentation layer, so a setting applies to both.
skull gopher://gopher.floodgap.com
skull gemini://geminiprotocol.net
Two render modes, switched at `skull://smallweb/` or with `gS`:
terminal full width, monospaced, a caret on every link (default)
page a comfortable measure, read like a document
Gemini has no certificate authorities. The first time a host is seen its
certificate is remembered, and a different one later is reported rather than
accepted. This is trust on first use. `skull://smallweb/` lists the remembered
hosts and forgets any of them, which trusts the next certificate afresh.
Gemini needs LuaSec (the `ssl` module). Without it the scheme still loads but
every page explains what to install. `setup-deps.sh` installs it.
## Tests
make run-tests
Requires `luassert`. The suite covers the Lua modules and the C bindings.
## Security model
Worth reading before you trust this with anything.
### What it does by default
third-party cookies blocked
DNS prefetching off
WebGL, WebAudio off
media capture off
clipboard access by JS denied
popups by JS denied
developer tools off
page permissions denied
typed addresses https, never http
Referer, cross-domain dropped
Referer, same domain cut back to the origin, no path or query
Referer on a downgrade dropped
files written on disk 0600, the process runs under umask 0077
ad and tracker lists EasyList, EasyPrivacy, EasyList Portuguese
### What it does not do
There is no sandbox around Lua. A module under `~/.config/skull/` runs in the
browser process with every power your user account has: it can read your files,
open sockets and start programs. That is the same deal as a window manager
config, and it is what makes the browser worth using, but it means **a Lua
module you did not read is a program you did not read**.
The same applies to userscripts installed with `:usinstall`. They reach the
bridge into Lua, so treat one from a web page as you would treat a shell script
from a web page.
JavaScript is on by default. Turn it off globally or per domain in `rc.lua`:
settings.webview.enable_javascript = false
settings.on["example.com"].webview.enable_javascript = true
Saved form data, including passwords, is stored in `~/.local/share/skull/`
as plain Lua that the browser executes on every page load. The file is 0600
and the pattern is anchored to the page host, but it is not encrypted. If that
matters to you, use a password manager and do not use `:formfiller`.
### Certificates
When a certificate fails verification you can trust it anyway. That choice
covers one certificate on one host, not the host in general, so a different
certificate on the same host warns again. The exception lapses after 90 days.
gC list the stored exceptions
:certs same thing
While an exception is in force the status bar shows `(exception)` rather than
`(trust)`, because the connection is not verified, it is excused.
### Reporting something
Open an issue at <https://github.com/runawaydevil/skull-browser/issues>. If it
is a vulnerability rather than a bug, say so in the title and leave out the
working payload until it is fixed.
## Extending
Everything above the C core is Lua, and the Lua is on your disk.
lib/ the modules that make up the browser
lib/*_wm.lua modules that run inside WebKit's render process
lib/lousy/ widgets and utilities shared by the rest
config/rc.lua what gets loaded, and in what order
widgets/ the C widgets exposed to Lua
clib/ the C libraries exposed to Lua
A module is an ordinary Lua file returning a table. Drop it in
`~/.config/skull/` and `require` it from your `rc.lua`.
An internal page takes about ten lines:
local chrome = require "chrome"
chrome.add("hello", function ()
return "<html><body><h1>hello</h1></body></html>"
end)
That gives you `skull://hello/`. Pass a table of functions as the fourth
argument to `chrome.add` and they become callable from the page's JavaScript,
returning promises. Only pages served from `skull://` can reach them.
Key bindings and commands go through `modes`:
local modes = require "modes"
modes.add_binds("normal", {
{ "^gh$", "Go home.", function (w) w:navigate("skull://newtab/") end },
})
modes.add_cmds({
{ ":hello", "Say hello.", function (w) w:notify("hello") end },
})
Type `:help` in the browser for the generated documentation, which covers
every module, binding and setting present in your build.
`docs/decisions/` holds the decisions that shaped the fork and the reasoning
behind them. `001-webkitgtk-api.md` explains why nothing new should be written
under `extension/`.
## Contributing
make build
make run-tests the whole suite
make apidoc regenerate doc/apidocs
The suite includes style checks. `luacheck` runs over every Lua file, trailing
whitespace fails the build, and every exported module function needs a doc
comment. Run the tests before sending anything; CI runs the same commands on
push and on pull requests.
Tests live in `tests/async/`, one file per module, each returning a table of
`test_*` functions. A fix belongs with a test that fails without it.
Commit messages say what changed and why, in English, present tense, with a
short subject line and a body when the subject is not enough.
## License
GNU GPLv3. See `COPYING.GPLv3`.
Based on luakit. All prior authorship is preserved in `AUTHORS`.
|