Logo

index : blog

---

  • summary
  • about
  • tree
  • log
  • branches
https://git.ptrace.dev/public/blog.git
ssh://git@git.ptrace.dev/public/blog.git
JAI: 55%
HTM: 10%
PNG: 8%
PY: 6%
OTHER: 21%

Static Blog Generator for ptrace.dev

This is a very tailored html generator and search server for my blog.
In a sense where it probably won't fit your needs. But you're welcome to explore the code and use parts of it.

Since I wanted to avoid writing 30 lines of Javascript, I opted for writing +1600 lines of Jai code to have a search function.

Search Server

The search server is build with sockets and a very, very minimal HTTP implementation. Since it's so low-level, there's no hand holding in regards of security and stability. To guarantee those attributes, you need to make some things happen:

  • Test the connection with several types of clients (described below)
  • Load test via wrk & libfuzz
  • Sandbox with seccomp, landlock, and namespaces (more details below)

Clients

The code would be smaller, if all clients would've a good connection. But the reality is different. Clients could be:

  • Slow
  • Dripping (giving us a few bytes every n times)
  • Stalling (just refuse to send ACKs back)
  • Spammy (flood us with requests aka DoS or dDoS)

And the next issue is, how you design your server. This server is synchronous and single threaded. Which sounds horrible at first, but you'll be surprised how performative it is and it solves the mentioned problems!

Epoll

If you design your server just with the classic accept() and recv(), clients will block your server thread. Resulting in a unresponsive website for other clients, since your code will block at recv()!

One might just™ open new threads, but depending on your machine, this concept will fail very quickly.

But we can add epoll() into the mix!

Instead of waiting till the client send its data, the kernel does the waiting and returns data that is ready to process.
I recommend to look into man epoll for more details.

Timeout

Clients are not stalling our thread anymore, but they still could linger around and waste a file descriptor. For this case, you have to implement a timeout, which disconnects the client after some time.

Malformed Requests

This server expects this http request:

GET /search?q=<search term> HTTP/1.x

You could do the full validation routine, that checks every method kind. You could also implement some logic that handles the HTTP version.
Or you could ask yourself, what does my server really need?

If you know what requests you expect, you can omit whole chunks of code, which is also easier to test.

That being said, I know that my server only accepts GET requests. And it doesn't support long-living connections. It's just request in, response out, and close connection aka HTTP/1.0.

The validation for that becomes really simple. You can straight up check if the request even starts with GET or not, and reject it early before do any parsing.

Having this tiny validation routine, makes it easier to spot bugs.

Now we can add fuzzing. I'm using LLVMs libfuzz to perform tests on certain functions. Which reveals, if your program blows up or does not handle specific szenarios right.

Sandboxing

Since anyone can throw requests towards my server, it is vital to not only ensure your server handles weird requests, it's also important to handle the case where someone took over the process because of some unknown vulnerability in your program.

In this case, we use tools to achieve a sandboxed environment.

The easiest way would be, to use systemd.exec and just start your program in such restrictive environment.

If you don't like the dependency of systemd you could also implement the sandboxing by yourself. Or implement partial sandboxing and close gaps with systemd - which was my approach.

I'm using two concepts for restricting my program:

  • seccomp
  • landlock

There's also a third concept, called namespaces. But for this tiny project I didn't implemented it in code, since it required way more care and loc (Which is the reason why I reached for systemd.exec).

Markdown Metadata

At the top of the markdown file we have to declare some metadata.
This is how an entry looks like:

hidden: false
width: 700
title: test image
published: 2026-01-01-12-00
updated: now
META;

Some text that gets rendered as html later.

Lets walk through each element.

hidden [Optional]

If hidden: true is provided it will ignore this entry entirely.
Providing hidden: false or omitting it entirely will render it.

width [Optional]

Some blog posts are using code blocks where the block will overflow with the standard width. You can set the width in pixel individually per blog post here.

title

The title of the blog post will be presented as big orange text.

published

When the blog post will be published as: YYYY-MM-DD-H-M
You could omit hours and minutes and just supply YYYY-MM-DD

It is also possible to omit it or use now - in both cases it will use the current date.

After running this program it will add the datetime as milliseconds.

updated

When the article was updated the last time. You could also omit it or use now like mentioned above.

META;

This is important to close our 'header' with META; so it gets parsed correctly.
If this is missing, the program will complain.

Known Issues

  • 2026-04-27: When viewing a search result, that contains an image, it does not load it, since the path is wrong. This happens because documents_html: *[]Entry is not filtered, so it doesn't contain updated image paths.
EXPAND COLLAPSE
Name
Size
Modified (UTC)
.gitignore
148 b
2026-05-21
raw
LICENSE
265 b
2026-04-29
raw
README.md
6 kB
2026-04-29
raw
---
---
logo.png
5 kB
2026-04-29
raw
logo.svg
3 kB
2026-04-29
raw
---
---
graph_2026_04_22_01.png
437 kB
2026-04-29
raw
---
---
plot_261208_18534.png
51 kB
2026-04-29
raw
plot_261208_19551.png
65 kB
2026-04-29
raw
plot_302009_21330.png
56 kB
2026-04-29
raw
plot_324521_22875.png
58 kB
2026-04-29
raw
plot_336875_23267.png
35 kB
2026-04-29
raw
plot_418257_27374.png
54 kB
2026-04-29
raw
plot_metrics.gp
655 b
2026-04-29
raw
record.py
4 kB
2026-04-29
raw
build.jai
10 kB
2026-06-17
raw
---
---
blog.ptrace.dev.conf
51 b
2026-05-15
raw
print_all_syscalls.py
491 b
2026-04-29
raw
ptrace_search_server.service
59 b
2026-05-15
raw
push.py
38 b
2026-05-15
raw
server.py
3 kB
2026-04-29
raw
service.sh
41 b
2026-05-15
raw
syscalls.py
3 kB
2026-04-29
raw
---
---
---
---
01
15 b
2026-04-29
raw
02
30 b
2026-04-29
raw
03
17 b
2026-04-29
raw
04
70 b
2026-04-29
raw
05
29 b
2026-04-29
raw
---
---
01
25 b
2026-04-29
raw
02
34 b
2026-04-29
raw
03
17 b
2026-04-29
raw
04
80 b
2026-04-29
raw
05
29 b
2026-04-29
raw
---
---
01
19 b
2026-04-29
raw
02
30 b
2026-04-29
raw
03
32 b
2026-04-29
raw
04
21 b
2026-04-29
raw
e2e.cfg
178 b
2026-04-29
raw
generate_corpus.py
2 kB
2026-04-29
raw
request.cfg
206 b
2026-04-29
raw
search.cfg
122 b
2026-04-29
raw
---
---
---
---
lib.jai
3 kB
2026-04-29
raw
---
---
COPYING
8 kB
2026-04-29
raw
libcmark-gfm.a
629 kB
2026-04-29
raw
libcmark-gfm.so
337 kB
2026-04-29
raw
libcmark-gfm.so.0.29.0.gfm.13
337 kB
2026-04-29
raw
module.jai
19 b
2026-04-29
raw
---
---
lib.jai
15 kB
2026-04-29
raw
module.jai
17 b
2026-04-29
raw
---
---
LICENSE
3 kB
2026-04-29
raw
README.md
3 kB
2026-04-29
raw
example.jai
9 kB
2026-04-29
raw
module.jai
17 kB
2026-04-29
raw
---
---
lib.jai
3 kB
2026-04-29
raw
module.jai
17 b
2026-04-29
raw
---
---
.editorconfig
465 b
2026-04-29
raw
.gitignore
8 b
2026-04-29
raw
LICENSE
2 kB
2026-04-29
raw
char_class.jai
6 kB
2026-04-29
raw
compile.jai
30 kB
2026-04-29
raw
first.jai
2 kB
2026-04-29
raw
match.jai
6 kB
2026-04-29
raw
module.jai
496 b
2026-04-29
raw
nfa.jai
11 kB
2026-04-29
raw
parse.jai
86 kB
2026-04-29
raw
program.jai
35 kB
2026-04-29
raw
readme.md
3 kB
2026-04-29
raw
rune.jai
5 kB
2026-04-29
raw
simplify.jai
14 kB
2026-04-29
raw
sparse.jai
3 kB
2026-04-29
raw
---
---
---
---
add_range.spec.jai
3 kB
2026-04-29
raw
find_range.spec.jai
801 b
2026-04-29
raw
negate.spec.jai
3 kB
2026-04-29
raw
050_parse.spec.jai
2 kB
2026-04-29
raw
100_compile.spec.jai
2 kB
2026-04-29
raw
200_match.spec.jai
4 kB
2026-04-29
raw
unicode_casefold.jai
15 kB
2026-04-29
raw
unicode_group.jai
124 kB
2026-04-29
raw
walk.jai
3 kB
2026-04-29
raw
---
---
LICENSE
32 kB
2026-04-29
raw
---
---
basics.jai
829 b
2026-04-29
raw
fuzzer_e2e.jai
343 b
2026-04-29
raw
fuzzer_request.jai
396 b
2026-04-29
raw
fuzzer_search.jai
511 b
2026-04-29
raw
---
---
blog.h
982 b
2026-04-29
raw
blog.jai
14 kB
2026-05-21
raw
constants.h
4 kB
2026-04-29
raw
entries.h
177 b
2026-04-29
raw
entries.jai
6 kB
2026-05-15
raw
main.jai
6 kB
2026-06-17
raw
pages.jai
4 kB
2026-06-17
raw
parser.jai
3 kB
2026-04-29
raw
replace.jai
8 kB
2026-04-29
raw
update.jai
7 kB
2026-04-29
raw
marshal.jai
848 b
2026-04-29
raw
quick_trace.jai
2 kB
2026-04-29
raw
---
---
http.jai
19 kB
2026-04-29
raw
liblandlock.jai
10 kB
2026-04-29
raw
libseccomp.jai
16 kB
2026-04-29
raw
main.h
225 b
2026-04-29
raw
main.jai
12 kB
2026-05-02
raw
reload.jai
3 kB
2026-04-29
raw
response.jai
822 b
2026-04-29
raw
search.jai
6 kB
2026-05-02
raw
sec_landlock.jai
793 b
2026-05-02
raw
sec_seccomp.jai
2 kB
2026-05-02
raw
---
---
entries_all.html
522 b
2026-05-21
raw
entries_limited.html
831 b
2026-05-21
raw
entry.html
421 b
2026-05-06
raw
generic_page.html
255 b
2026-04-29
raw
index.css
11 kB
2026-05-21
raw
index.html
798 b
2026-04-29
raw
overview.html
401 b
2026-04-29
raw
overview_limited.html
705 b
2026-04-29
raw
search.html
499 b
2026-04-29
raw
search_item.html
444 b
2026-05-06
raw
search_response.html
86 b
2026-04-29
raw
template.md
30 b
2026-05-01
raw
---
---
backend_curl.jai
6 kB
2026-04-29
raw
backend_socket.jai
3 kB
2026-04-29
raw
main.h
2 kB
2026-04-29
raw
main.jai
2 kB
2026-04-29
raw
Copyright 2026  E766CB298A6D1E64 | Git-Thing heavily inspired by cgit