CLI tool
imtomt/ymawky avatar
imtomt/ymawky

ymawky: a web server written by hand in ARM64 assembly

MacOS Web Server written entirely in ARM64 assembly

579 stars23 forksAssemblyGPL-3.0

At a glance

What is it?
ymawky is a MacOS and Linux web server written entirely in ARM64 assembly with no libc, using raw syscalls and forking a process per connection, serving static files and running CGI scripts. It suits someone who wants to read a complete server in one language, and the author is clear that it is a fun project with unknown vulnerabilities.
Who is it for?
ymawky fits a developer who wants to read an entire web server end to end in assembly, or who wants a dependency-free static server with CGI on a Mac, since the whole implementation is syscall-only and links against nothing but the system library.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 122 days ago.
What is it written in?
Mainly Assembly, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

No libc, just syscalls, and a process per connection

ymawky, which the readme spells out phonetically as yuh maw kee, is a web server written entirely in ARM64 assembly. It is a syscall-only server with no libc, forking per connection, written by hand for MacOS and Linux, with a separate Linux branch in the same repository. That combination of properties is what makes the project interesting to read. There is no library underneath it doing the work of opening sockets, parsing headers or managing memory, so everything the server does is visible in the source, and the Makefile confirms how little is involved. The link step pulls in the system framework and the macOS SDK path, sets the entry point and builds for arm64. The compile step is a plain cc invocation on a single assembly file, and the link step names the entry point and the architecture, so the whole build configuration is short enough to read in one sitting.

make
	cc -g -c $< -o $@
	ld $(OBJS) -o ymawky $(LDFLAGS)

The source lives in a src directory of.S files, with the wildcard picked up automatically by the Makefile, and the tree is small enough that the whole project is a reasonable afternoon of reading.

Building needs the Xcode command line tools and Apple silicon

The build requirements are short. You need the Xcode Command Line Tools, installed with xcode-select --install, and ymawky only runs on Apple silicon, meaning arm64. Then you run make to build. The output is a single executable named ymawky, and the Makefile removes the intermediate object files as part of the link step so the tree does not accumulate them. A clean target removes the binary and the objects. Beyond the executable, the server expects two directories next to it. A www directory is the document root where ymawky searches for files, and a GET with an empty filename searches for www/index.html, so you probably want an index.html in there. An err directory holds the static error pages, and the server looks for them at err/(code).html, so a 404 or a 500 gets your own HTML rather than a bare status line. There is also a build_err_pages.sh script at the top of the repository, and the config file is config.S, referenced from the readme as the place to change the default file and document root.

Running it, and the debug mode that any odd argument triggers

There are three ways to start the server. Running it with no arguments starts it on 127.0.0.1:8080. Passing a port starts it on that port instead. Passing literally any character other than a digit starts it on 127.0.0.1:8080 in debug mode, and that mode disables forking so ymawky handles a single request, which the author says he needed because lldb was not letting him debug the children. Custom addresses are not supported, and the readme is candid that this is only because it has not been implemented, adding that if you would like to consider it a safety feature then it could be intentional. So the server is loopback only, which is worth knowing before you try to reach it from another machine. To see it working, you start it with a port, open a browser or use curl, and visit 127.0.0.1:8080/ or 127.0.0.1:8080/pretty/index.html. The readme closes that section by inviting you to bask in the warmth of assembly.

The HTTP surface is wider than a static file server would suggest

The readme presents the server as a dynamic web server, and the supported methods list is broader than you might expect. GET, PUT, DELETE, OPTIONS and HEAD are handled directly, and POST works through CGI scripts. There is basic protection from slowloris-like denial of service attacks. The server decodes percent hex encoding, so %20 becomes a space in a filename and %61 becomes a. It serves directory listings, and it does MIME type detection to set the Content-Type in the response. Range requests are supported in GET, with full ranges of the form bytes=X-N, suffix ranges of bytes=-N and open-ended ranges of bytes=X-, and the readme notes that video scrubbing is well supported as a result. HTTP version parsing is basic but enforced: a request has to specify HTTP/1.1 or HTTP/1.0, and an HTTP/1.1 request needs a Host field present in the header, which the readme attributes to RFC 9112 Section 3.2, even though ymawky does nothing with Host. Error codes can be served with your own pages from the err directory, and there is a build_err_pages.sh script in the repository.

Path traversal handling, and the distinction it turns on

The path handling is the part of the readme with the most examples, and the examples are the point. The server blocks .. from traversing paths while still allowing multiple periods when they are part of a filename. So a request for ../../../etc/passwd gets 403 Forbidden, a request for /ohwell...txt gets 200 OK, a request for /../src/ymawky.S gets 403 Forbidden, and a request for /hehe..txt gets 200 OK. That last pair is the interesting one, because a naive filter that rejects any path containing two consecutive dots would also break perfectly ordinary filenames. The safety section lists the whole set of precautions. Paths of 4096 bytes or more are rejected. Any path including path traversal is rejected, written as /../... Requests that do not contain a path within 16 bytes are rejected. The server is confined to the www directory, since www is prepended to every requested path. Any path containing symlinks is rejected, using O_NOFOLLOW_ANY. Empty GET requests default to GET www/index.html, and GET /index.html retrieves www/index.html.

Uploads are atomic, and the temporary file is protected too

PUT is the most carefully engineered part of the server, and the readme treats it that way. PUT requests support uploads of up to 1GiB, though the limit can be configured for larger files. PUT is atomic because the data is written to a temporary file and then renamed, which allows concurrent PUT requests without leaving partially written files behind. Content-Length parsing and verification is done on PUT requests. The temporary file is named www/.ymawky_tmp_ followed by the process id, and after the whole file arrives successfully it is renamed to the requested filename, so a partial or corrupt request cannot overwrite an existing file. The temporary path is itself protected, because any request whose path starts with www/.ymawky_tmp_ is rejected, which stops someone from GETting a temporary file or from sending a PUT to /.ymawky_tmp_4533 or similar. Data has to arrive within 10 seconds, and the entire header has to be received within 10 seconds in total, and the connection is closed otherwise.

CGI works, and the readme tells you when not to use it

CGI support is the feature that turns a static file server into a dynamic one, and the readme is frank about its limits. CGI scripts must live within CGI_DIR, defined in config.S and defaulting to (docroot)/cgi-bin/. Query strings are supported, ymawky parses the script's headers and forwards them to the client response, and it enforces some minimal compliance, since all scripts must begin their response with a header and, if there is a body, the header must contain a Content-Length field. The HTTP response code comes from the script's Status header, so a script can send its own 404 or 500, and a default of 200 OK is used when no Status is given. The warning is the important part. CGI support is currently experimental and does not have the same strict timeout settings as PUT, so a script could loop forever, read input forever or hang, and ymawky will not kill it. The readme's advice is direct: you should not run ymawky on a real server, but if you have to, remove the www/cgi-bin directory and do not allow CGI support.

Editorial conclusion

ymawky fits a developer who wants to read an entire web server end to end in assembly, or who wants a dependency-free static server with CGI on a Mac, since the whole implementation is syscall-only and links against nothing but the system library. It does not fit a production deployment, because the author describes it as a fun project that is probably full of vulnerabilities he is unaware of, CGI support is experimental with no timeout, and it binds only to the loopback address. Before you run it beyond your own machine, read the safety list and check each item against your threat model, remove the cgi-bin directory if you do not need it, and remember that any non-numeric argument starts the server in debug mode, which disables forking and handles a single request.

Frequently asked questions

What is ymawky?

It is a web server written entirely in ARM64 assembly, described as a syscall-only server with no libc that forks a process per connection, written by hand for MacOS and Linux with a separate Linux branch in the repository.

How do I build and run ymawky?

Install the Xcode Command Line Tools with xcode-select --install, run make to build, and then run ./ymawky to start on 127.0.0.1:8080, or ./ymawky with a port argument to choose a different port. ymawky only runs on Apple silicon, and it needs a www directory next to the executable as its document root.

What does debug mode do in ymawky?

Passing any argument that is not a digit starts the server in debug mode on 127.0.0.1:8080. Debug mode disables forking and makes ymawky handle a single request, which the author needed because lldb was not letting him debug the forked children.

How does ymawky handle PUT uploads?

PUT supports uploads of up to 1GiB and is atomic, because the data is written to a temporary file named www/.ymawky_tmp_ followed by the process id and then renamed to the requested filename, so concurrent uploads cannot leave partially written files. Requests for paths starting with www/.ymawky_tmp_ are rejected, and the data must arrive within 10 seconds.

Is ymawky safe to run on a public server?

The author does not think so, describing it as a web server written entirely by hand in ARM64 assembly as a fun project that is probably full of vulnerabilities he is unaware of. He adds that you should not run ymawky on a real server, and that if you have to you should remove the www/cgi-bin directory and not allow CGI support.

Official sources

  1. imtomt/ymawky on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/imtomt-ymawky.svg)](https://hysenlabs.com/projects/imtomt-ymawky)

Was this useful?