# Amethyst Web Server ## A word of warning! Currently Amethyst is in very early alpha stage, a lot of things will be broken, names won't be correct, promised features missing, but I'm very much working on it live! Every save I do increments the build number by 1, I won't publish all of them, but some of them will be published. Once a milestone is hit (e.g. a new feature fully implemented), I'll publish a release! ## Currently working features: * New configuration is ~95% done, most features work. * Fixed **A LOT** of unreported bugs from the old code. * More resilliency against errors. * Improved security. * Proxy almost working! ## Project status: Amethyst will stay in beta for a while, I want all features to work, but I will make pre-release versions that are mostly stable. They can be found as the `amethyst-prerel-0.a.b` releases. I won't guarantee 100% stability, but waay more than just some random build. ## Install instructions: Install Python, execute `amethyst.py` and change the provided config. ## Minimum requirements: Python 3.10+ And whatever PC that happens to run that. I recommend Python 3.12 or above though, with a PC running: * Windows 8.1+ * macOS 10.15+ * Linux 4.19+ * FreeBSD 13.2R+ * Some other somewhat recent OS. ## The webserver itself: The Amethyst webserver is meant to be easy to use and configure. Its configuration takes inspiration from nginx and Caddyfile. The language the configuration is made in is AmethystConf. The default config is as follows: ```amethystconf host * { directory:./html pesmode:0 block-ua:match("Discordbot") index:index.html } globals { http:1 https:1 port:8080 https-port:8443 key:./key.pem cert:./cert.pem max-length:8192 } ``` It uses a key-value syntax, and uses a `:` as its seperator. A few key directives: `host`, followed by a hostname signifies a host that will be available. Similar to nginx's `server_name` directive. `globals` signifies all values that are of global importance, like the key and certificate file. `directory` signifies the directory to look in for files on that specific host. `index`, while not in the default config, signifies what path should be returned if the client only asks for `/` (or any subpaths without files). `pesmode` signifies if the PES mode must be enabled, allowing the server to run custom Python code to manipulate the request or file further. `block-ua` signifies if a specific (or loosely matched) User-Agent must be blocked from accessing the site. `proxy` signifies if a the server needs to get the response from a different (remote) server but still needs to be available at this host. `max-length` signifies the maximum length a request may have. AmethystConf has only 4 datatypes: `String`, `Boolean`, `Function` and `None`. A quick rundown: `String` is the everything datatype. Everything is assumed to be a `String` unless it falls under the other categories. `Boolean` is the datatype used to enable/disable features. A `Boolean` can have one of two possible values: `1` or `0`. `Function` is the datatype used in `match()`, it signifies that the parser has to do some work on this string before it can use it. `None` is the datatype assigned to any key without a value. ## PES (Python Extension Script) The PES (Python Extension Script) is one of Amethysts main selling points. It's a new type of a dynamic page. A PES file is pretty much a Python script with some conventions. Currently it is in very alpha form. It will be heavily improved upon to make sure even people with no Python knowledge can work with it. Here's how it works: If PES mode is enabled, the request is sent to the `pes.py` script, more specifically, the `on_request(req)` function of the `PES()` class. From there, the decoded request is given to you to play with. All of Amethysts request and file processing tools are available, and soon a function will be added to hand the request back to Amethyst, if it is deemed not suitable for PES mode. Once you're done manipulating the request, all you have to do is call `return self.build_response(http_status_code, resp_body, mimetype)` and Amethyst will handle the rest. A few examples of what can be achieved with PES mode without writing any other language than Python, HTML and CSS: * Showing a random image from the `/pics` folder upon requesting `/randompic` from the server * Dynamically updating the time on a website * Create a complex calculator * Upload files to the server * Lock down webpages with a login prompt. * And much, much more. While it might not be able to create truly dynamic pages (since PES runs server-side, not client-side), it is dynamically static, basically, it's a dynamic page until it's rendered in browser, where it's static, as PES cannot change anything there. **WARNING!** PES is an advanced feature! You can absolutely compromise the security of your webserver by having a misconfigured PES file. While Amethyst still has a few protection measures built-in that activate before any request reaches the PES, but some are bypassed unless manually invoked in the PES. Because of that, here's a general user advisory: * Use `self.fh.read_file(file_path, host=None)` instead of `open(file_path)` because of file inclusion or directory traversal concerns. * Use `self.fh.write_file(file_path, host=None)` instead of `open(file_path)` because of file inclusion or directory traversal concerns. * **NEVER** allow the PES to run shell code! * **NEVER** allow the PES to run **ANYTHING** uploaded via the internet! * Try running as much of the code locally, getting data from the internet can not only take long, it can also pose a security risk. The PES will **NOT** warn you if you have security issues, it's a very hands-off approach. The PES will happily run `sudo rm -rf / --no-preserve-root` if given the command and setup for shell execution and not tell you until everything is gone. Prevent those scenarios by limiting what PES does as much as possible!