130 comments

[ 5.0 ms ] story [ 184 ms ] thread
That's insane and impressive. Well done!
Really cool concept and execution. I did notice that the live notifications quickly become overwhelming, so there should be some kind of rate limiter on them.
I could see "@User123 has entered the thread" working in the smaller forums I was a part of where I knew everyone. phpBB/vBulletin show a "Users reading this thread: ..." list for that reason, and there were popular plugins for things like "@Foo and @Bar are writing responses to this thread". It helped make the place feel more alive, especially when you were in an intense discussion. It was nice to know the fker was working on his next big quote-by-quote debunk of your post.

But getting a notification when unregistered guests click into a thread doesn't seem useful to anyone.

Tap the Bell icon to disable the live notifications.
I couldn't read the article on mobile due to live notifications.
Tap the Bell icon to disable the live notifications.
I can’t do that either because the notifications pile up and cover the button.
Dear AsmBB.org admin: Have those be turned off by default. They're annoying even when they don't occlude the entire screen.
Especially for guests viewing the page. No one really needs to know that a random person loaded the page. If there is some kind of friend/follow functionality for logged in users, and they want to enable notifications for them, ok, but that’s about as far as it should ever go.
Why would anyone care to know that an anonymous person is reading the same page as you?
Reminds me a little of D's official forums, which load faster than you can close your eyes to blink. I love these kind of projects.
Oh yeah it's immediately noticeable. How did they do it?

https://forum.dlang.org/

Use a fast language and forget some of the old advice from "12 factor apps," some of which are focused on avoiding common challenges when scaling low-performance platforms.
Which ones did you have in mind?
D isn’t “big” (as in “reddit big”), and any web-1.0 forum that I remember from 2000s worked like that on old hardware. If you’re curious how a single SQL database can serve so fast, it’s caches and invalidation when a thread CrUDs itself. Old boring tech.

Edit: the tone was a little sharp, edited it out, my apologies

It's not just written in assembly, but it's backed by sqlite. Like many apps, IO is more likely to be the bottleneck than the CPU.
Using WAL mode with PRAGMA synchronous set to NORMAL, or even in that case? (I'm the furthest from a SQLite expert, just looking at it myself for a project.)
RAM is a factor too. I’ve written some programs for Arduino in C that would be a good match for assembly because the stack and C calling conventions are a complete waste. (e.g. sometimes you have no recursive functions). The thing is portability —- as much as I love AVR-8 if my requirements grow AVR-8 has no nowhere to go except maybe an FPGA soft core. In C I can switch to an ARM microcontroller which I find really uninspiring but just performs better.

That’s the one trouble w/ AsmBB that it is specific to some particular architecture.

That's often the fallacy people have, assuming that the language is a bottleneck instead of the I/O or database design.

Mind you, I understand it given some of the PHP I've seen. But again, that's not the language per se, but how it's used.

Man how do you even connect to a db with assembly code? Are there assembly libraries?

In theory I understand it but the monumental amount of effort it would take to write such “simple” things would take so much time

It says that the DB is SQLite, so I presume they’re just linking to the SQLite libraries.
It's less complicated than you might think. You call into SQLite C library functions using C calling conventions. Here's the relevant code: https://asm32.info/fossil/asmbb/file?name=source/sqlite3.asm...
How about for setting up a server and serving http requests / establishing web sockets?

If it’s just a bunch of calling C code then I have to ask which part of this is actually assembly

To be fair, from that perspective every backend web application in every language is really just "calling C code".
That's overly simplistic and untrue. If you write a C# / .Net program you're not "just calling C code", you're not calling C at all.
but then your runtime VM calls C :)
But the CLR is written c, c++ and assembly which is what runs c#. Also the syscalls that run down the chain are also written in c, c++ etc.
That is just the runtime. That is smaller of fraction of what is going on when your programs execute than you think.

The C# code you write (and majority of the your code's dependencies) are JIT compiled to native machine code (or more recently there are AOT compilation options).

You can ask the same about e.g. Pascal, Python and Javascript, because all of them just make a bunch of C calls at the end of the day and can’t use NIC ports directly.
To do this in assembler you write raw syscalls to bind to open the TCP stream and then accept to handle incoming connections, possibly with fork or clone3 to spawn a process/thread to handle the new connection. None of that requires C programming. HTTP is "just" some text parsing on top of read/write calls to the file descriptor returned by accept. Web sockets are "just" some connection logic on top of HTTP.

A basic implementation of that is totally possible in assembly with no calls to c libraries. Would even be a great project for people to learn some systems programming. Keep in mind, all of these protocols (except maybe web sockets) were designed when programming in assembler was common. There's probably some HTTP implementation in the wild doing the same thing.

That said there's a lot of reasons you probably don't want to do that, HTTP can be subtle and you probably don't want to be doing raw clone3 calls to spawn threads, if you want to be doing thread or process per connection in the first place.

Well, you see, most OSes today aren't written in ASM. Their native libraries are C, so you have to interface with those libraries if you want to utilize them and their features.

You can certainly write your own TCP/IP stack with raw sockets and directly interface with the NIC, if you like. But at that point you're both reinventing the wheel and bypassing the entirety of the OS, you might as well just write a forum exokernel.

Most developers don't consider utilizing a library in another language as "cheating". Popular and core Rust, D, C++, Python, nodeJS, etc libraries do this all the time.

C is generally first compiled into assembly anyways.

When you program in assembly you're basically just doing the job of the compiler, and can call all the same library functions C code can. You just have to follow the calling conventions of the architecture. It's cumbersome and tedious, but nowhere near impossible.

x64 made calling functions more annoying by using registers then spilling over into the stack after exhausting those. x32 was more pleasant by far, since it only used the stack. x64's convention produces faster code though, by keeping stuff in registers.

> x32 was more pleasant by far, since it only used the stack

Maybe I misunderstand you, but this is completely dependent on compiler, operating system etc, there are multiple calling conventions for x86-32, eg. cdecl, fastcall ...

https://en.wikipedia.org/wiki/X86_calling_conventions

Apologies, my experience is SysV-centric for amd64, as described here[0] under the Parameter Passing section, which is a clustrfuck compared to the simplicity of the 386 SysV ABI as described here[1]:

  > Argument words are pushed onto the stack in reverse order (that is, the
  > rightmost argument in C call syntax has the highest address), preserving the
  > stack’s word alignment. All incoming arguments appear on the stack, resid-
  > ing in the stack frame of the caller.
Both links were found @ https://wiki.osdev.org/System_V_ABI

[0] https://www.uclibc.org/docs/psABI-x86_64.pdf

[1] https://www.sco.com/developers/devspecs/abi386-4.pdf

> C is generally first compiled into assembly anyways.

I don't think that's right. C is compiled into an intermediate representation first. It's true that you can ask GCC etc. to generate assembly output, but it's not the default.

> I don't think that's right. C is compiled into an intermediate representation first. It's true that you can ask GCC etc. to generate assembly output, but it's not the default.

Just because there's an IR within the compiler doesn't mean there's no "assemble" stage. What do you think the "-pipe" flag to gcc is for? It pipes the compiler output into the assembler, rather than using a temporary .s file.

gcc has four main stages going from source to executable: preprocessor->compiler->assembler->linker.

Edit, for your convenience, gcc -save-temps example:

  > [user@host /tmp/foo]$ ls
  > f.c
  > [user@host /tmp/foo]$ cat f.c
  > #include <stdio.h>
  >
  > int main(int argc, char **argv)
  > {
  >         puts("Hello world!");
  >
  >         return 0;
  > }
  > [user@host /tmp/foo]$ gcc -o f -save-temps f.c
  > [user@host /tmp/foo]$ ls -l
  > total 48
  > -rwxr-xr-x 1 user user 15416 Jan 13 16:50 f
  > -rw-r--r-- 1 user user    91 Jan 13 16:49 f.c 
  > -rw-r--r-- 1 user user 17145 Jan 13 16:50 f.i 
  > -rw-r--r-- 1 user user  1496 Jan 13 16:50 f.o 
  > -rw-r--r-- 1 user user   515 Jan 13 16:50 f.s 
  > [user@host /tmp/foo]$ cat f.s
  >         .file   "f.c"
  >         .text
  >         .section        .rodata
  > .LC0:
  >         .string "Hello world!"
  >         .text
  >         .globl  main
  >         .type   main, @function
  > main:
  > .LFB0:
  >         .cfi_startproc
  >         pushq   %rbp
  >         .cfi_def_cfa_offset 16
  >         .cfi_offset 6, -16
  >         movq    %rsp, %rbp
  >         .cfi_def_cfa_register 6
  >         subq    $16, %rsp
  >         movl    %edi, -4(%rbp)
  >         movq    %rsi, -16(%rbp)
  >         leaq    .LC0(%rip), %rax
  >         movq    %rax, %rdi
  >         call    puts@PLT
  >         movl    $0, %eax
  >         leave
  >         .cfi_def_cfa 7, 8
  >         ret
  >         .cfi_endproc
  > .LFE0:
  >         .size   main, .-main
  >         .ident  "GCC: (GNU) 13.1.1 20230429"
  >         .section        .note.GNU-stack,"",@progbits
  > [user@host /tmp/foo]$ ./f
  > Hello world!
  > [user@host /tmp/foo]$ 
  >
Thanks for the correction, I assumed the assembly stage was not done.
there are compilers that do not generate textual assembly output from their ir, but gcc in particular does
i suspect you mean i386, not x32; x32 is an almost unused abi added in 02011 for 64-bit amd and compatible processors, available with gcc -mx32. https://lwn.net/Articles/548838/ talks a bit about its early adoption

it's so little used that i can't find the abi documentation, but gcc -c -mx32 does seem to generate code that passes arguments in registers, not on the stack, contrary to your assertion

edit: after enough digging, i found https://web.archive.org/web/20130902094821/https://sites.goo... linking to https://web.archive.org/web/20201015005434/https://3263a7b2-... but x32-abi-1.0.pdf seems to be missing from the archive and https://web.archive.org/web/*/https://sites.google.com/site/...* doesn't look at all promising

You're right, I was sloppy in typing x32 for i386 (as you mentioned, nobody actually uses that).
cool, thanks for clarifying
In the header, there’s a button to disable live notifications.

On a side note, showing a list of forum users to someone who isn’t logged in is probably a bad idea. Is it something configurable?

Haven’t found the button to disable live notifications while on mobile.
Welcome to the internet young one. That is what forums do since forever. And probably configurable as well.
Processing time is insanely fast according to the footer. But the time it takes to transfer the document to Denmark is 500 - 1000ms. Seems like a CDN is better than performant code in this case... Still an impressive feat.
I'm in the Azores islands, in the middle of the Atlantic Ocean, and it loaded in under 500ms.
Latvia here, 256ms according to FF
Coming from leading response time initiatives at a Big Tech, ouch. 1s is painful. Obviously transit time is a big part of that but if that's considered "good", well oof.
Well your average big tech site might transfer in a fraction of that, but then spends >5 seconds running JavaScript before anything useful happens.
What, are you implying that staring at grey circle and square placeholders for 15 seconds isn't good user experience? That's crazy talk!
You see, the client side metrics has the transfer time at 2ms and time to first interaction (onMouseOver event) at 5ms, and that aligns with the developer's experience on his M3 Mac on a network that's literally next door to the servers. :(
Despite being someone who wrote x86 assembly for years on DOS in the 90s before learning C (and even continued in at&t syntax on Linux for some time), I find this to be a silly exercise in time wasting beyond its academic and comedic/entertainment value.
"silly exercise in time wasting beyond its academic and comedic/entertainment value"

I love this as it expresses how I feel about JavaScript :P

This is super cool, though I very much doubt the following statement:

> AsmBB is very secure web application, because of the internal design and the reduced dependencies.

There's a lot of value in using well tested dependencies, and even Chuck Norris will have bugs writing complex software in assembly. Especially when doing string manipulation which this project should be doing a lot of.

I was rubbed the wrong way with that line too, such claims usually set off red flags, but the OP appears to not write English natively so they might have legitimately missed out some tempering.

e.g. "AsmBB focuses on security in its design and reduced reliance on dependencies."

I disagree; dependencies are more of a liability than anything else.
I can see this maybe being true in a high level language (depending on the dependency), though it's definitely not the case with assembly.

OpenSSL, for example, was hit with quite a few serious bugs over the years, and I'd still choose using it than my own SSL implementation. Especially in assembly.

> OpenSSL, for example, was hit with quite a few serious bugs over the years, and I'd still choose using it than my own SSL implementation. Especially in assembly.

That's the difference between a programmer and a real engineer: an engineer doesn't need any help from dependencies to make his code totally insecure.

Engineers do design, implementation is the job of the technician or machinist or somebody like that. If the engineer’s code ends up shipped to customers, something very strange has happened.
In my world architects design, engineers provide feedback and implement stuff

First time I hear about technicians writing code.

the source code is the design; it plays the same role that schematics or mechanical drawings play for products made by electronic technicians or machinists. the difference is that compilers take the place of technicians or machinists, and that confuses people, often for political/ideological reasons

see jack w. reeves's 'code as design' series, beginning with 'what is software design?' from 01992, for a fuller explanation https://www.developerdotstar.com/mag/articles/reeves_design.... https://www.developerdotstar.com/mag/articles/reeves_origina... https://www.developerdotstar.com/mag/articles/reeves_13years..., and the discussion on wiki https://wiki.c2.com/?TheSourceCodeIsTheDesign=

That was a great read. Amazing how it can still be quoted to explain software design 32 years later.
glad you enjoyed it!
No snark but this is the ultimate example of “tell me you never built software without telling me you never built software”

Unless I totally missed the sarcasm.

people who are not familiar with cryptography are often unaware of this, but cryptographic algorithms are among the kinds of code most commonly written in assembly

not only are they almost never subject to the kinds of security problems we commonly associate with low-level languages like assembly or c (for example, buffer overflows and heap corruption) they also commonly need to run in constant time to prevent timing side-channel information leaks, and while that is not trivial to achieve in any language, it's more feasible in assembly than in c. also, they are commonly a performance bottleneck, and they commonly need kinds of bit manipulations that c is typically slow at, which rewards assembly implementations

openssl in particular contains about 85000 lines of assembly language, mostly embedded in perl files. (it uses perl as a sort of alternative macro assembler)

I'm aware, and this is a good point to bring up about cryptography. I had the protocol code in mind when I was talking about the bugs. I should have used curl as example in retrospect. :)
i agree that probably assembly is an especially bug-prone language to implement things like protocol logic and asn.1 parsing; but high-level programming languages are pretty bad at things like that, too. it still mostly depends on your system design: assembly might be a perfectly fine language in which to implement a bytecode interpreter for a safe asn.1 parser in a non-turing-complete language you can formally verify. assembly's weak points don't matter much for bytecode interpreters

incidentally those 85000 lines of assembly are about 12% of the total openssl codebase

Do you really want to implement http, ssl and tls; all by your self, what about security hash algorithms? Re inventing the wheel means you also get to go through all the bugs and s purity issue that these open source libraries went through. This is a type of opinion I would expect some one with no understanding about security saying.
Assuming you do have enough understanding of secure programming, you can actually forgo most dependencies if you really want. You can ignore most edge cases which have to be implemented if you exactly follow the standard (e.g. HTTP parsing rules), supporting only one ciphersuite that is known to be safe and widely used, and so on. Of course that still doesn't justify the use of assembly.
If you're building an application that depends heavily on networking for either its performance or reliability, then yes, you are better off rewriting those components and taking on ownership of hardening them to satisfy your requirements than you are relying on someone else that doesn't know your use case and has no incentive to making it work well making sure it works well for you.

HTTP is fairly trivial as well; if you're afraid of little things like that, you'll never deliver best-in-class software.

> even Chuck Norris will have bugs writing complex software in assembly.

Surely you mean CPUs will have bugs executing Chuck Norris' impeccable assembly code.

When Chuck Norris writes complex software in assembly, bugs apologize to Chuck for letting themselves getting carried away, fix their erratic behavior, and promise to never let that happen again.
Chuck Norris does not use an assembler. He live codes machine language directly into DRAM.

He deal with security threats at the source, by tracking down the hacker and kicking them in the head.

"He deal with security threats at the source, by tracking down the hacker and kicking them in the head."

Not just any kicks, but roundhouse kicks of course.

i agree that assembly is more bug-prone than other languages, but things like internal design and dependencies make a bigger difference. if all your strings are managed with your custom library for dynamically allocated strings, you probably aren't going to have any string buffer overflows because there are only like three places where you could get it wrong. on the other hand, if you're bringing in megabytes of libc code full of string handling, there's lots of potential for bugs

in this case, though, they're linking with sqlite, so all that is out the window

as far as i know, though it's a lot simpler than asmbb, httpdito doesn't have any security holes: http://canonical.org/~kragen/sw/dev3/server.s http://canonical.org/~kragen/sw/dev3/httpdito-readme

as 10000truths points out in https://news.ycombinator.com/item?id=38985198, in assembly you don't have to deal with undefined behavior, and that helps a lot. occasionally you have behavior that varies by implementations, but there's no nasal demons, and in particular you can add two unknown integers without even hitting implementation-defined behavior. and, in theory, you can never guarantee that a c program won't overflow its stack, and i've had this happen in practice on arduino, where it collided with the heap. and signedness bugs (which are commonly security holes; even qmail had one) are plausibly easier to avoid in assembly than in c, though recent compiler versions help with that

This comment was the first thing that popped up in my head when I saw the title of this post. I remember thinking the exact same when this project was part of a CTF I played, which just hosted the latest version of this project. At least 8 vulnerabilities were found during the competition.

https://ctftime.org/task/24399

Whos' Chuck Norris ?
That answered my question.

Chuck Norris is a martial artist that had an acting career. He got a second wind of fame when he became a meme… Chuck Norris Facts. https://en.wikipedia.org/wiki/Chuck_Norris_facts

"Chuck Norris's Social Security number is the last nine digits of pi."
More correctly, he's a clown that had a clowning career.
Most popular dependencies aren't well tested because of the constant scope creep. And if they didn't constantly scope creep people would talk about it being "dead" because the last commit was 1+ year ago.
I like this, and kudos for going for assembly language. It is certainly true that reducing dependencies reduces the attack surface for something, that does not, in and of itself, make it "secure". But yes, in a probabilistic sort of way it reduces the chances of exploits. Of course assembly language has zero memory safety or other guarantees which probabilistically increases risk. I can't say if I think they balance out one way or the other. Still, I love me some assembly language applications. For extra credit transpile it to aarch64 and then you can run it on a Pi-Zero (or Pi-W) in a wall wart :-).

I have been researching, off and on, distributed forums. As forums like the one created here became the "go to" after Usenet fell out of favor I have thought a replacement would have both the distribution/replication features of Usenet and the user experience of phpBB (the grandparent of most forum software).

Making it distributed, and eventually consistent, is an excellent distributed systems challenge. So it is kind of like relaxing to a puzzle. If you, gentle reader, have written something along those lines I'd love to have a look.

> Of course assembly language has zero memory safety or other guarantees which probabilistically increases risk. I can't say if I think they balance out one way or the other.

I would posit that assembly + Linux kernel ABI is safer than the traditional C/C++ stack because they are not littered with nearly as much "undefined behavior". Signed arithmetic overflows and underflows as expected. Memory allocation with mmap + MAP_ANONYMOUS initializes it to 0 as expected. Accessing unmapped memory in your address space (including address 0) triggers a SIGSEGV as expected. Your assembler makes much fewer assumptions than your C compiler and doesn't try to be half as clever, so it's more likely to crash and burn on error instead of silently subverting your expectations.

This isn't a problem in practice because C++ developers are expected to know the rules of their language and respect them.
I think you meant "in theory" instead of "in practice" :)
I think it was meant sarcastically
It wasn't sarcastic.

Being a C++ developer requires discipline, and that's why hiring is much more selective and specialized.

I'm certain "in practice" was intentional, after all this seems to be what C++ compiler developers expect :-P
> C++ developers are expected to know the rules of their language and respect them

In the same way children are expected to respect fire :-P

Maybe so but I wouldn't recommend running forum software written in C++ either!

"This footgun is safer than a footknife!"

This is interesting. The forum looks nice and loads quick for me. Great job
[flagged]
He's either bought the propaganda, or he's pretending to have bought the propaganda for safety reasons.
True, but it's a tricky time to point that out. Right now we'd have to boycott a large proportion of open source projects by Israeli and Arab/Iranian/etc authors if we wanted to avoid using any open source software written by people who support obscene and inhumane military actions.
True. Worthwhile contemplating how to navigate this sort of thing.
I don’t think there is any point in boycotting open-source software written by those people?

If it was actually their source of income, then it might do something, but it’s already freely available.

It influences the personal psychological state of people to see that they're being ostracized, especially if they are not accustomed to it, and hence statistically will lead some to some impact on the balance of public opinion and state positions via democratic and societal network mechanisms. I think that it's helpful to ensure that all Israeli authors understand that the majority of the western world is no longer prepared to excuse their country's barbarity, to the extent that they will shun projects by citizens. It may have less impact on Arab/Iranian etc supporters of radical anti-Israel military actions because they are already accustomed to being ostracized by the west.
If I was in Russia or otherwise had no other nationality, I might.

But I'd avoid the topic whether at all possible.

79.7 kB transferred. It's nice to see minimal pages being generated. Have to disable the notifications to see this result though.
You could do something similar in C and forgo the standard library. No dependencies other than syscalls. I don't really see a compelling reason to do it in assembly other than as an intellectual exercise.
“In addition it supports Unicode Emoji characters in really native way.”

I wish it would go into greater detail here. I’m unclear what “really native” implies, as opposed to some sort of “kind of native” way?

Just spit balling here: some sites like Twitter have their own emoji sets and I wonder if they're saying that they don't do that and instead rely on the OS's normal rendering of emojis.
It seems that AsmBB has absolutely no Emoji processing in its asm code, so it's more or less pass-through. Realtime chat does have emoji highlighting in its JS code (a part of the template) though:

    function formatEmoji(text) {
      var emojiRegEx = /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/g;
      return text.replace(emojiRegEx, '<span class="emoji"><span>$1</span></span>');
    }
This probably meant to catch U+00A9, U+00AE, U+2000..3300 and U+1F000..1FBFF, which is by itself too broad, but also the regex itself is not a faithful translation. (If anyone is wondering the correct solution, just use emoji-regex [1] or more recent `/\p{RGI_Emoji}/v` pattern [2].)

[1] https://unpkg.com/browse/emoji-regex/index.js

[2] https://caniuse.com/mdn-javascript_builtins_regexp_unicodese...

Next: Unikernel
Yes please! Still waiting for unikernels to actually become a thing. Looks so promising, but Unikraft has yet to launch their cloud offering and NanoVM is 7$ per unikernel, which, compared to an always on shared Hetzner vCPU for ~4$ (knowing those two are not exactly the same) is kind of pricey... Would expect them to be cheaper or at least cost the same
Oh, I had the impression NanOS was free.
I think you're right, but I'm mostly interested in the cloud offerings. It's super hard to convince anyone to roll your own unikernel infrastructure
Sorry - just now seeing this.

We don't have any $7/unikernel offerings out there at all. We do have a desktop app that costs $7.

As for your other comment - one of the really awesome things about unikernels is that you don't have to roll your own infrastructure.

The way these are made is that all the infrastructure management is pushed onto the cloud of choice.

I'd highly encourage you to if you haven't to download https://ops.city and try to deploy something to GCP or AWS or wherever - it's free and would answer a lot of questions/assumptions you might have.

[dead]
> x86 Linux server

How hard would it be to add ARM support?

That's the thing about Assembly, you rewrite the program.
You've discovered why it's not a great idea to write your servers in Assembly.
Needs X86 Linux hosting

Won't run on nearlyfreespeech.net hosting

not even under qemu-user?
I'd be curious what the outcome of extensive fuzz testing would be. I'm sure there's many subtle bugs to be found!
Missing in the headline is which asm, or even what OS it runs under.

"x86 asm" and "on linux" are combined not larger than than "assembly language".

I wonder if there's a sort of "Ruby on Rails" clone in assembly? That sounds like a cool idea, but perhaps not that useful