Replace Astro/Strapi site with Next.js static export

- Fresh structure: Next.js App Router + Tailwind v4 at the repo root, exported
  as static files (output: export) and served by static-web-server
- Remove the Strapi CMS, Astro app, Pagefind search, comments, old scripts,
  Lighthouse config and compose files; content in content/ is now plain
  markdown with YAML frontmatter
- Pages: About, Writing, Reading (3D bookshelf) and Portfolio, styled after
  adammaj.com
- SEO: metadata and canonicals, JSON-LD, generated OG images, sitemap, robots,
  RSS and JSON feeds, llms.txt
- Umami via PUBLIC_UMAMI_SCRIPT_URL / PUBLIC_UMAMI_WEBSITE_ID (build args)
- Dockerfile builds the export and serves it with sws.toml (security headers,
  clean URLs, .well-known, OG image content type, immutable asset caching)
- Prettier and cspell for content with a husky/lint-staged pre-commit hook
- Old redirect list dropped to start from a clean slate
This commit is contained in:
2026-09-30 12:48:14 +03:00
parent cafcb5b4e0
commit ec6401ac38
227 changed files with 4357 additions and 27144 deletions
-118
View File
@@ -1,118 +0,0 @@
# content/
This directory is the canonical source of truth for all site content. It is
shared between `apps/site` (Astro reads it directly at build time) and
`apps/cms` (Strapi's custom provider reads/writes these same files — its
SQLite database is only a disposable cache/index, never the source of
record). The site must always be buildable straight from this directory with
Strapi entirely absent.
Frontmatter is TOML (`+++ ... +++`), matching the legacy Zola posts under
`content/posts/` to ease migration. Each content type below is one file (or a
directory with `index.md` for entries with co-located assets), one frontmatter
schema.
## `writings/` (blog / essays)
```toml
+++
title = "..."
description = "..."
author = "andwati"
date = 2026-04-08
updated = 2026-04-10 # optional, only when the post has been revised
draft = false
canonical_url = "" # optional, only if first published elsewhere
[taxonomies]
tags = ["..."]
[extra]
series = "..." # optional
series_index = 1 # required if `series` is set
llms_description = "..." # optional per-post override of the site-wide one
+++
```
## `portfolio/` (projects / professional work)
```toml
+++
title = "..."
description = "..."
role = "..."
date_start = 2025-01-01
date_end = 2025-06-01 # optional, omit if ongoing
outcome = "one-line result"
draft = false
[[links]]
label = "Live"
url = "https://..."
[[links]]
label = "Source"
url = "https://..."
[taxonomies]
tags = ["..."] # stack / tech tags
+++
```
Body is a free-form Markdown case study.
## `bookshelf/` (books *and* papers)
`kind` selects which identifier fields apply — books use `isbn`, papers use
`doi`/`arxiv_id`. Book covers are fetched from Open Library by ISBN and
cached into `apps/site/public/covers/` (served at `/covers/...`), never
fetched at page-render time. Papers have no reliable cover source, so
`cover_image` is set manually for them, if at all; Semantic Scholar is only
used to backfill a missing title/authors by DOI/arXiv id.
Note: the field is named `read_status`, not `status` — Strapi's
content-manager API treats a top-level `status` key as its own reserved
draft/publish parameter even with `draftAndPublish` disabled, so an
attribute literally named `status` silently breaks writes. Don't rename it
back.
```toml
+++
title = "..."
kind = "book" # "book" | "paper"
authors = ["..."]
isbn = "9780000000000" # books only
doi = "10.1000/xyz123" # papers only
arxiv_id = "2101.00001" # papers only, optional
url = "https://..." # papers only: landing page / source link
cover_image = "/covers/slug.jpg"
rating = 5 # optional, 1-5
read_status = "read" # "reading" | "read" | "abandoned"
date_started = 2026-01-01
date_finished = 2026-01-20 # optional, omit if still in progress
[taxonomies]
tags = ["..."] # genre / topic
+++
```
Body is a free-form personal review/notes.
## `blogs/` (curated external blogs)
```toml
+++
title = "..." # the blog/site's name
url = "https://..."
feed_url = "https://.../rss.xml"
+++
```
Body is a one-to-two sentence blurb on why you follow it. No live feed
fetching — this list is hand-curated and static.
## Markdown extras (all body content)
- **LaTeX**: `$inline$` and `$$block$$` math renders to static HTML/MathML
at build time (`marked-katex-extension` in `toml-content-loader.ts`) — no
client-side JS shipped for it.
- **Callouts**: `{% <note> %} ... {% </note> %}` (also `tip`/`warning`/
`danger`) renders as a styled callout box.
- **YouTube embeds**: paste a plain `<iframe src="https://www.youtube-
nocookie.com/embed/VIDEO_ID" ...></iframe>` — it's automatically wrapped
in a responsive 16:9 container, gets `loading="lazy"`, and emits
`VideoObject` structured data. No shortcode needed.
-6
View File
@@ -1,6 +0,0 @@
+++
template = "index.html"
page_template = "page.html"
+++
spend my free time working on hobby projects and sometimes I write about my experiences. In particular, I enjoy learning about offensive security and reverse engineering.
+25 -23
View File
@@ -1,31 +1,33 @@
+++
title = "About"
template = "page.html"
+++
---
title: "About"
---
spend my free time working on hobby projects and sometimes I write about my experiences. In particular, I enjoy learning about offensive security and reverse engineering.
# Ian Andwati
## PGP
## Some things about me:
- I'm a software engineer by day. Cyber Security is my hobby and my side work.
- I have a degree in computer science, and most of what I do for fun still centres on it.
- I'm the captain of the [**binarysith**](https://ctftime.org/team/379200) CTF team. I play CTFs on the weekends under the alias **mockingspectre**.
- I like infrastructure-level software: the code that other code runs on.
- I write essays on software development, from personal experience to opinion pieces (with varying degrees of "success"). The blog has been going since 2021 and has moved around a lot: different domains, Medium, dev.to, Hashnode. This site is where I keep the originals.
## Some things I'm interested in:
- The intersection of software engineering and cyber security.
- Low-level programming, because it's where performance and security meet.
- Distributed systems and performance at scale.
- Compiler design.
- Database internals.
- Reading, fiction and non-fiction alike, especially the classics.
- Art, history and science.
Check out my [reading](/reading), [writing](/writing) and [portfolio](/portfolio).
### PGP
For anything sensitive, you can reach me encrypted.
```txt
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEalcjtBYJKwYBBAHaRw8BAQdA55X1mRCKw9DA35kCAy+7Z30ye0rUHWBZsl7O
Wmar+RS0IklhbiBBbmR3YXRpIDxhbmR3YXRpaWFuQGdtYWlsLmNvbT6IkwQTFgoA
OxYhBK1YqreiDYUjy/WaeR8g/tCqBbDaBQJqVyO0AhsDBQsJCAcCAiICBhUKCQgL
AgQWAgMBAh4HAheAAAoJEB8g/tCqBbDace8BAN1SpkEMSYll+XupKHiaIUwnhm4N
eS79G1i+345Gl6CyAQDjgn+3rzG6sexjKh+10EKiy5omsQEGzUW03s7gS0T1Drg4
BGpXI7QSCisGAQQBl1UBBQEBB0CXofBIYYXG2+dZqgllhsbkNPF4qlyfPgbRW+zb
7Kw3IAMBCAeIeAQYFgoAIBYhBK1YqreiDYUjy/WaeR8g/tCqBbDaBQJqVyO0AhsM
AAoJEB8g/tCqBbDauMkA/RG4iqtPZOI3DQzFJYu7SnxaD81vdP0LyTLKDQkynySu
AQDrlWyF8LRlad4iX1yQPCuOV0aUpTNq5kPlWurtX99/BQ==
=uKsS
-----END PGP PUBLIC KEY BLOCK-----
```
- Fingerprint: `AD58 AAB7 A20D 8523 CBF5 9A79 1F20 FED0 AA05 B0DA`
- Public key: [/.well-known/pgp-key.txt](/.well-known/pgp-key.txt)
- Also on: [keys.openpgp.org](https://keys.openpgp.org/search?q=AD58AAB7A20D8523CBF59A791F20FED0AA05B0DA) and [keyserver.ubuntu.com](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0xAD58AAB7A20D8523CBF59A791F20FED0AA05B0DA)
- Also see: [/.well-known/security.txt](/.well-known/security.txt) (signed)
@@ -1,17 +0,0 @@
+++
title = "Attention Is All You Need"
kind = "paper"
authors = ["Ashish Vaswani", "Noam Shazeer", "Niki Parmar"]
doi = "10.48550/arXiv.1706.03762"
arxiv_id = "1706.03762"
url = "https://arxiv.org/abs/1706.03762"
cover_image = "/covers/attention-is-all-you-need.jpg"
rating = 5
read_status = "read"
date_started = 2026-02-01
date_finished = 2026-02-03
[taxonomies]
tags = ["machine-learning", "papers"]
+++
Placeholder review establishing the `bookshelf` schema for `kind = "paper"`.
@@ -1,15 +0,0 @@
+++
title = "Designing Data-Intensive Applications, 2nd Edition"
kind = "book"
authors = [ "Martin Kleppmann", "Chris Riccomini" ]
isbn = "9781098119058"
url = "https://www.oreilly.com/library/view/designing-data-intensive-applications/9781098119058/"
cover_image = "/covers/designing-data-intensive-applications-2nd-edition.webp"
rating = 5
read_status = "reading"
[taxonomies]
tags = [ "distributed-systems" ]
+++
@@ -1,15 +0,0 @@
+++
title = "Structure and Interpretation of Computer Programs"
kind = "book"
authors = ["Harold Abelson", "Gerald Jay Sussman"]
isbn = "9780262510875"
cover_image = "/covers/structure-and-interpretation-of-computer-programs.jpg"
rating = 5
read_status = "read"
date_started = 2026-01-01
date_finished = 2026-01-20
[taxonomies]
tags = ["computer-science", "programming"]
+++
Placeholder review establishing the `bookshelf` schema for `kind = "book"`.
+11 -15
View File
@@ -1,15 +1,11 @@
+++
title = "Example Project"
description = "Placeholder portfolio entry establishing the schema."
role = "Author"
date_start = 2026-01-01
outcome = "Established the portfolio content schema."
draft = false
[[links]]
label = "Source"
url = "https://github.com/andwati"
[taxonomies]
tags = ["placeholder"]
+++
Placeholder case study body establishing the `portfolio` content schema.
---
title: "Example Project"
description: "Placeholder portfolio entry establishing the schema."
role: "Author"
dateStart: "2026-01-01"
outcome: "Established the portfolio content schema."
links:
- label: "Source"
url: "https://github.com/andwati"
tags: ["placeholder"]
---
@@ -0,0 +1,11 @@
---
title: "Attention Is All You Need"
kind: "paper"
authors: ["Ashish Vaswani", "Noam Shazeer", "Niki Parmar"]
url: "https://arxiv.org/abs/1706.03762"
coverImage: "/covers/attention-is-all-you-need.jpg"
rating: 5
status: "read"
dateFinished: "2026-02-03"
tags: ["machine-learning", "papers"]
---
@@ -0,0 +1,10 @@
---
title: "Designing Data-Intensive Applications, 2nd Edition"
kind: "book"
authors: ["Martin Kleppmann", "Chris Riccomini"]
url: "https://www.oreilly.com/library/view/designing-data-intensive-applications/9781098119058/"
coverImage: "/covers/designing-data-intensive-applications-2nd-edition.webp"
rating: 5
status: "reading"
tags: ["distributed-systems"]
---
@@ -0,0 +1,10 @@
---
title: "Structure and Interpretation of Computer Programs"
kind: "book"
authors: ["Harold Abelson", "Gerald Jay Sussman"]
coverImage: "/covers/structure-and-interpretation-of-computer-programs.jpg"
rating: 5
status: "read"
dateFinished: "2026-01-20"
tags: ["computer-science", "programming"]
---
@@ -1,14 +1,9 @@
+++
title = "A Fresh Coat Of Paint"
description = "Migrating to Zola"
author = "mockigspectre"
date = 2023-08-24
draft = false
[taxonomies]
tags = [ "zola", "blog", "tera", "rust" ]
+++
---
title: "A Fresh Coat Of Paint"
description: "Migrating to Zola"
date: "2023-08-24"
tags: ["zola", "blog", "tera", "rust"]
---
Using Zola as my preferred static site generator
@@ -1,18 +1,13 @@
+++
title = "From C to Machine Code: What Actually Runs"
description = "Before diving into how to break a program, we need to understand what the computer is actually executing"
author = "andwati"
date = 2026-04-08
draft = false
---
title: "From C to Machine Code: What Actually Runs"
description: "Before diving into how to break a program, we need to understand what the computer is actually executing"
date: "2026-04-08"
tags: ["assembly", "rev", "pwn"]
---
[taxonomies]
tags = [ "assembly", "rev", "pwn" ]
<div class="youtube"><iframe src="https://www.youtube-nocookie.com/embed/KbH239zvDtM" title="YouTube video" loading="lazy" allowfullscreen></iframe></div>
+++
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/KbH239zvDtM" title="YouTube video" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
Before diving into how to break a program, we need to understand what the computer is actually executing. When you write a C program, the processor doesn't understand a single line of it. The C language exists for *our* benefit, allowing us to write logic without having to manually shuffle bits around in the processor.
Before diving into how to break a program, we need to understand what the computer is actually executing. When you write a C program, the processor doesn't understand a single line of it. The C language exists for _our_ benefit, allowing us to write logic without having to manually shuffle bits around in the processor.
To bridge this gap, we use a compiler (like `GCC`), which translates our human-readable C code into machine code—raw binary instructions. Assembly language is simply the human-readable representation of that machine code.
@@ -20,43 +15,44 @@ Because we usually don't have the original C source code when analyzing malware
Let's look at what a basic `printf("Hello World!");` program actually turns into.
```c
```c
#include <stdio.h>
int main(){
printf("hello world\n");
return 0;
}
```
We will compile the hello world C program, then dump the assembly in Intel syntax(it defaults to AT&T syntax, a cancer).
int main(){
printf("hello world\n");
return 0;
}
```
```sh
gcc -o hello hello.c && objdump -d -M intel hello | grep -A 15 "**<main>:**"
We will compile the hello world C program, then dump the assembly in Intel syntax(it defaults to AT&T syntax, a cancer).
```sh
gcc -o hello hello.c && objdump -d -M intel hello | grep -A 15 "**<main>:**"
```
<br>
```asm
0000000000001139 <main>:
1139: 55 push rbp
113a: 48 89 e5 mov rbp,rsp
113d: 48 8d 05 c0 0e 00 00 lea rax,[rip+0xec0] # 2004 <_IO_stdin_used+0x4>
1144: 48 89 c7 mov rdi,rax
1147: e8 e4 fe ff ff call 1030 <puts@plt>
114c: b8 00 00 00 00 mov eax,0x0
1151: 5d pop rbp
```asm
0000000000001139 <main>:
1139: 55 push rbp
113a: 48 89 e5 mov rbp,rsp
113d: 48 8d 05 c0 0e 00 00 lea rax,[rip+0xec0] # 2004 <_IO_stdin_used+0x4>
1144: 48 89 c7 mov rdi,rax
1147: e8 e4 fe ff ff call 1030 <puts@plt>
114c: b8 00 00 00 00 mov eax,0x0
1151: 5d pop rbp
1152: c3 ret
Disassembly of section .fini:
0000000000001154 <_fini>:
1154: 48 83 ec 08 sub rsp,0x8
1158: 48 83 c4 08 add rsp,0x8
0000000000001154 <_fini>:
1154: 48 83 ec 08 sub rsp,0x8
1158: 48 83 c4 08 add rsp,0x8
115c: c3 ret
```
Notice how different this looks from C. The processor executes different instruction sets depending on its architecture. The two most common architectures you will encounter are 32-bit (x86) and 64-bit (x86_64 or just x64/AMD64/Intel 64).
Notice how different this looks from C. The processor executes different instruction sets depending on its architecture. The two most common architectures you will encounter are 32-bit (x86) and 64-bit (x86_64 or just x64/AMD64/Intel 64).
## **The Processor's Workbench: Registers**
@@ -64,9 +60,9 @@ To process data rapidly, the CPU doesn't want to constantly reach all the way ou
Here are the critical x64 registers you need to know:
* **`rip` (Instruction Pointer):** The most important register for exploitation. It points to the exact memory address of the *next* instruction the CPU is supposed to execute.
* **`rsp` (Stack Pointer):** Points to the very top of your current stack frame.
* **`rbp` (Base Pointer):** Points to the bottom of your current stack frame.
- **`rip` (Instruction Pointer):** The most important register for exploitation. It points to the exact memory address of the _next_ instruction the CPU is supposed to execute.
- **`rsp` (Stack Pointer):** Points to the very top of your current stack frame.
- **`rbp` (Base Pointer):** Points to the bottom of your current stack frame.
### **General Purpose Registers & Calling Conventions**
@@ -74,16 +70,18 @@ There are also general-purpose registers (`rax`, `rbx`, `rcx`, `rdx`, `rsi`, `rd
In x64 Linux, when a function is called, the arguments are passed directly into registers in this specific order:
1. `rdi` (First argument)
2. `rsi` (Second argument)
3. `rdx` (Third argument)
4. `rcx` (Fourth argument)
5. `r8` (Fifth argument)
1. `rdi` (First argument)
2. `rsi` (Second argument)
3. `rdx` (Third argument)
4. `rcx` (Fourth argument)
5. `r8` (Fifth argument)
6. `r9` (Sixth argument)
{% <note> %}
<aside class="callout callout-note">
In older x86 32-bit binaries, arguments aren't passed in registers at all; they are pushed onto the stack. When a function finishes and returns a value, that return value is almost always placed in the **`rax`** register (or `eax` in 32-bit).
{% </note> %}
</aside>
### **Register Sizes and "Words"**
@@ -98,29 +96,28 @@ Registers have different sizes depending on the architecture, and you can refere
Here is how x64 registers break down into smaller accessible chunks:
| 8-Byte (Qword)[64 bit] | Lower 4-Bytes (Dword)[lower 32 bits] | Lower 2-Bytes (Word)[lower 16 bits] | Lower 1-Byte[lower 8 bits] |
| :---- | :---- | :---- | :---- |
| rax | eax | ax | al |
| rbx | ebx | bx | bl |
| rcx | ecx | cx | cl |
| rdx | edx | dx | dl |
| rdi | edi | di | dil |
| rsi | edi | si | sil |
| rsp | esp | sp | spl |
| rbp | ebp | bp | bpl |
| rip | eip | - | - |
| r8 | r8d | r8w | r8b |
| r9 | r9d | r9w | r9b |
| r10 | r10d | r10w | r10b |
| r11 | r11d | r11w | r11b |
| r12 | r12d | r12w | r12b |
| r13 | r13d | r13w | r13b |
| r14 | r14d | r14w | r14b |
| r15 | r15d | r15w | r15b |
| :--------------------- | :----------------------------------- | :---------------------------------- | :------------------------- |
| rax | eax | ax | al |
| rbx | ebx | bx | bl |
| rcx | ecx | cx | cl |
| rdx | edx | dx | dl |
| rdi | edi | di | dil |
| rsi | edi | si | sil |
| rsp | esp | sp | spl |
| rbp | ebp | bp | bpl |
| rip | eip | - | - |
| r8 | r8d | r8w | r8b |
| r9 | r9d | r9w | r9b |
| r10 | r10d | r10w | r10b |
| r11 | r11d | r11w | r11b |
| r12 | r12d | r12w | r12b |
| r13 | r13d | r13w | r13b |
| r14 | r14d | r14w | r14b |
| r15 | r15d | r15w | r15b |
<br>
<p>If you are dealing with a 32-bit (x86) binary, the 8-byte 'R' registers don't exist. The largest buckets you have are the 4-byte 'E' registers (`eax`, `ebp`, etc.).</p>
## **The Stack**
The stack is a segment of memory heavily used during execution. It's primarily used to store local variables, keep track of function calls, and manage control flow. It operates as a LIFO (Last-In, First-Out) structure—meaning you can only add (push) to the top of it, and remove (pop) from the top of it.
@@ -179,13 +176,14 @@ Disassembly of section .fini:
1160: 48 83 ec 08 sub rsp,0x8
```
<br>
In your output, you'll see an instruction moving the value `0x5` into a memory location relative to the base pointer, usually something like `[rbp-0x4]`. That is your local variable resting on the stack.
## **The Heap**
We will just do an overview of this section. It can get quite complex very fast. We will eventually cover them in future articles.
We will just do an overview of this section. It can get quite complex very fast. We will eventually cover them in future articles.
If the stack is the CPU’s fast, tightly organized workbench, the heap is the massive, slightly chaotic warehouse out back.
@@ -199,28 +197,28 @@ In C, this is done using the `malloc()` (memory allocate) and `free()` functions
Consider heap.c
```c
#include <stdlib.h>
```c
#include <stdlib.h>
#include <string.h>
int main() {
// Ask the warehouse for 64 bytes of space
char *buffer = (char *)malloc(64);
// Put some data into that space
strcpy(buffer, "hello heap");
// Give the space back to the warehouse
free(buffer);
return 0;
int main() {
// Ask the warehouse for 64 bytes of space
char *buffer = (char *)malloc(64);
// Put some data into that space
strcpy(buffer, "hello heap");
// Give the space back to the warehouse
free(buffer);
return 0;
}
```
```
Now we do the necessary:
```sh
```sh
gcc -o heap heap.c && objdump -d -M intel heap | grep -A 20 "<main>:"
```
@@ -228,23 +226,23 @@ gcc -o heap heap.c && objdump -d -M intel heap | grep -A 20 "<main>:"
<br>
```asm
0000000000001149 **<main>:**
1149: 55 push rbp
114a: 48 89 e5 mov rbp,rsp
114d: 48 83 ec 10 sub rsp,0x10
1151: bf 40 00 00 00 mov edi,0x40
1156: e8 e5 fe ff ff call 1040 <malloc@plt>
115b: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
115f: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
1163: 48 ba 68 65 6c 6c 6f movabs rdx,0x6568206f6c6c6568
116a: 20 68 65
116d: 48 89 10 mov QWORD PTR [rax],rdx
1170: c7 40 07 65 61 70 00 mov DWORD PTR [rax+0x7],0x706165
1177: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
117b: 48 89 c7 mov rdi,rax
117e: e8 ad fe ff ff call 1030 <free@plt>
1183: b8 00 00 00 00 mov eax,0x0
1188: c9 leave
0000000000001149 **<main>:**
1149: 55 push rbp
114a: 48 89 e5 mov rbp,rsp
114d: 48 83 ec 10 sub rsp,0x10
1151: bf 40 00 00 00 mov edi,0x40
1156: e8 e5 fe ff ff call 1040 <malloc@plt>
115b: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
115f: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
1163: 48 ba 68 65 6c 6c 6f movabs rdx,0x6568206f6c6c6568
116a: 20 68 65
116d: 48 89 10 mov QWORD PTR [rax],rdx
1170: c7 40 07 65 61 70 00 mov DWORD PTR [rax+0x7],0x706165
1177: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
117b: 48 89 c7 mov rdi,rax
117e: e8 ad fe ff ff call 1030 <free@plt>
1183: b8 00 00 00 00 mov eax,0x0
1188: c9 leave
1189: c3 ret
Disassembly of section .fini:
@@ -262,54 +260,54 @@ Assembly might look like gibberish at first, but it essentially boils down to mo
### **Moving Data**
* **`mov dest, src`**: Copies data from the source to the destination. (`mov rax, rdx` copies the value in `rdx` into `rax`).
* **Dereferencing `[]`**: Brackets act like pointers in C. `mov rax, [rdx]` doesn't move `rdx` itself; it goes to the memory address stored in `rdx` and grabs the value sitting there. Conversely, `mov [rax], rdx` takes the value in `rdx` and writes it into the memory address pointed to by `rax`.
* **`lea dest, src`**: Load Effective Address. It calculates a memory address and stores the *address itself* in the destination, rather than the data at that address. (`lea rdi, [rbx+0x10]`).
- **`mov dest, src`**: Copies data from the source to the destination. (`mov rax, rdx` copies the value in `rdx` into `rax`).
- **Dereferencing `[]`**: Brackets act like pointers in C. `mov rax, [rdx]` doesn't move `rdx` itself; it goes to the memory address stored in `rdx` and grabs the value sitting there. Conversely, `mov [rax], rdx` takes the value in `rdx` and writes it into the memory address pointed to by `rax`.
- **`lea dest, src`**: Load Effective Address. It calculates a memory address and stores the _address itself_ in the destination, rather than the data at that address. (`lea rdi, [rbx+0x10]`).
### **Math and Logic**
* **`add dest, src` / `sub dest, src`**: Adds or subtracts the source from the destination, storing the result in the destination.
* **`xor dest, src`**: Performs a bitwise exclusive OR. (You will frequently see `xor rax, rax`—this is simply an optimized way to set `rax` to 0).
- **`add dest, src` / `sub dest, src`**: Adds or subtracts the source from the destination, storing the result in the destination.
- **`xor dest, src`**: Performs a bitwise exclusive OR. (You will frequently see `xor rax, rax`—this is simply an optimized way to set `rax` to 0).
### **Stack Operations**
* **`push reg`**: Subtracts from `rsp` to grow the stack, then places the register's value onto the new top of the stack.
* **`pop reg`**: Takes the value from the top of the stack, puts it into the register, and adds to `rsp` to shrink the stack.
- **`push reg`**: Subtracts from `rsp` to grow the stack, then places the register's value onto the new top of the stack.
- **`pop reg`**: Takes the value from the top of the stack, puts it into the register, and adds to `rsp` to shrink the stack.
### **Control Flow**
* **`jmp address`**: Unconditionally redirects code execution to a new memory address.
* **`call address`**: Used to invoke functions. It pushes the current `rip` onto the stack (so the program remembers where it was) and then jumps to the new address.
* **`ret`**: Pops the saved address off the stack and places it back into `rip`, seamlessly resuming execution where it left off before the `call`.
- **`jmp address`**: Unconditionally redirects code execution to a new memory address.
- **`call address`**: Used to invoke functions. It pushes the current `rip` onto the stack (so the program remembers where it was) and then jumps to the new address.
- **`ret`**: Pops the saved address off the stack and places it back into `rip`, seamlessly resuming execution where it left off before the `call`.
### **Conditionals and Flags**
The CPU has a special register dedicated to **Flags**—individual bits that indicate the state of the processor (e.g., the Zero Flag, Carry Flag, Sign Flag).
* **`cmp arg1, arg2`**: Compares two values by silently subtracting them. It doesn't save the mathematical result; instead, it updates the CPU Flags (like setting the Zero Flag if the two values were equal).
* **`jz` / `jnz`**: Jump if Zero / Jump if Not Zero. These look at the CPU flags set by a previous `cmp` instruction. If the Zero Flag is set, `jz` will take the jump. If not, the program just continues to the next instruction.
- **`cmp arg1, arg2`**: Compares two values by silently subtracting them. It doesn't save the mathematical result; instead, it updates the CPU Flags (like setting the Zero Flag if the two values were equal).
- **`jz` / `jnz`**: Jump if Zero / Jump if Not Zero. These look at the CPU flags set by a previous `cmp` instruction. If the Zero Flag is set, `jz` will take the jump. If not, the program just continues to the next instruction.
There is one register that contains flags. A flag is a particular bit of this register. If it is set or not, will typically mean something. Here is the list of flags.
```txt
00: Carry Flag
01: always 1
02: Parity Flag
03: always 0
04: Adjust Flag
05: always 0
06: Zero Flag
07: Sign Flag
08: Trap Flag
09: Interruption Flag
10: Direction Flag
11: Overflow Flag
12: I/O Privilege Field lower bit
13: I/O Privilege Field higher bit
14: Nested Task Flag
15: Resume Flag
00: Carry Flag
01: always 1
02: Parity Flag
03: always 0
04: Adjust Flag
05: always 0
06: Zero Flag
07: Sign Flag
08: Trap Flag
09: Interruption Flag
10: Direction Flag
11: Overflow Flag
12: I/O Privilege Field lower bit
13: I/O Privilege Field higher bit
14: Nested Task Flag
15: Resume Flag
```
<br>
There are other flags then the one listed, however we really don't deal with them too much (and out of these, there are only a few we actively deal with).
@@ -1,14 +1,9 @@
+++
title = "Django Secret Key Tutorial"
description = "Managing secrets"
author = "andwati"
date = 2022-11-11
draft = false
[taxonomies]
tags = [ "tutorial", "django", "python", ".env" ]
+++
---
title: "Django Secret Key Tutorial"
description: "Managing secrets"
date: "2022-11-11"
tags: ["tutorial", "django", "python", ".env"]
---
Managing the Django SECRET_KEY variable.
@@ -1,14 +1,9 @@
+++
title = "Hello World"
description = "Hello world"
author = "andwati"
date = 2022-11-04
draft = false
[taxonomies]
tags = [ "hugo", "blog", "go" ]
+++
---
title: "Hello World"
description: "Hello world"
date: "2022-11-04"
tags: ["hugo", "blog", "go"]
---
How it begins
@@ -1,14 +1,9 @@
+++
title = "cognitive inertia"
description = "inertia"
author = "andwati"
date = 2025-02-26
draft = false
[taxonomies]
tags = [ "cognition" ]
+++
---
title: "cognitive inertia"
description: "inertia"
date: "2025-02-26"
tags: ["cognition"]
---
Staring at the void
@@ -1,14 +1,9 @@
+++
title = "Install PostgreSQL on Arch | Manjaro | Garuda Linux"
description = "Getting PostgreSQL up and running on Arch Linux-based distros"
author = "andwati"
date = 2023-02-16
draft = false
[taxonomies]
tags = [ "postgresql", "archlinux", "manjaro", "garuda", "linux" ]
+++
---
title: "Install PostgreSQL on Arch | Manjaro | Garuda Linux"
description: "Getting PostgreSQL up and running on Arch Linux-based distros"
date: "2023-02-16"
tags: ["postgresql", "archlinux", "manjaro", "garuda", "linux"]
---
# Overview
@@ -46,7 +41,7 @@ Postgres offers a lot of advantages that make it an excellent choice for highly
Now with all that fluff out of the way, let's get to business. The steps have been tested on Arch Linux with the KDE desktop.
> **Note:** Commands that should be run as the *postgres* user are prefixed by `[postgres]$` in this article.
> **Note:** Commands that should be run as the _postgres_ user are prefixed by `[postgres]$` in this article.
## Step 1: Installing the PostgreSQL Package
@@ -54,7 +49,7 @@ Now with all that fluff out of the way, let's get to business. The steps have be
sudo pacman -Syu postgresql
```
This command will update the package database and update all packages on the system then install the PostgreSQL package from your distribution mirrors. Never install a package without updating the system first. On a rolling release, this can lead to an unbootable system. Installing the package will also create a system user called *postgres*. You can now switch to the *postgres* user using a privilege elevation program.
This command will update the package database and update all packages on the system then install the PostgreSQL package from your distribution mirrors. Never install a package without updating the system first. On a rolling release, this can lead to an unbootable system. Installing the package will also create a system user called _postgres_. You can now switch to the _postgres_ user using a privilege elevation program.
You can switch to the PostgreSQL user by executing the following command:
@@ -1,14 +1,9 @@
+++
title = "Integrating Netlify CMS"
description = "Integrating netlify cms"
author = "andwati"
date = 2022-11-24
draft = false
[taxonomies]
tags = [ "blog", "hugo", "netlify", "netlify-cms", "github" ]
+++
---
title: "Integrating Netlify CMS"
description: "Integrating netlify cms"
date: "2022-11-24"
tags: ["blog", "hugo", "netlify", "netlify-cms", "github"]
---
Migrating to a better content management system through Netlify CMS
@@ -1,14 +1,9 @@
+++
title = "Export default and module.exports in Javascript"
description = "Modules are the building blocks of modern JavaScript applications, allowing for the encapsulation of code into reusable components. Two primary systems govern the exportation of these modules: export default in ES6 and module.exports in CommonJS. "
author = "andwati"
date = 2024-01-17
draft = false
[taxonomies]
tags = [ "javascript", "node" ]
+++
---
title: "Export default and module.exports in Javascript"
description: "Modules are the building blocks of modern JavaScript applications, allowing for the encapsulation of code into reusable components. Two primary systems govern the exportation of these modules: export default in ES6 and module.exports in CommonJS. "
date: "2024-01-17"
tags: ["javascript", "node"]
---
Modules are the building blocks of modern JavaScript applications, allowing for the encapsulation of code into reusable components. Two primary systems govern the exportation of these modules: export default in ES6 and module.exports in CommonJS.
@@ -1,13 +1,9 @@
+++
title = "mmuctf 2025 writeup"
description = "mmuctf 2025 challenges writeup"
date = 2025-08-03
draft = false
[taxonomies]
tags = [ "ctf", "rev", "web", "forensics" ]
+++
---
title: "mmuctf 2025 writeup"
description: "mmuctf 2025 challenges writeup"
date: "2025-08-03"
tags: ["ctf", "rev", "web", "forensics"]
---
Over the weekend, I had the opportunity to participate in the mmuctf 2025. It was an individual jeopardy style CTF targeted towards beginners and intermediate people in cybersecurity. This is a write-up to explore some challenges I did, or at least almost did.
@@ -1,14 +1,9 @@
+++
title = "Setting up Obsidian sync in Windows"
description = "Syncing Obsidian in Windows with Windows Task Scheduler with git"
author = "andwati"
date = 2023-10-01
draft = false
[taxonomies]
tags = [ "obsidian", "git", "sync", "windows", "cron", "task-scheduler", "powershell" ]
+++
---
title: "Setting up Obsidian sync in Windows"
description: "Syncing Obsidian in Windows with Windows Task Scheduler with git"
date: "2023-10-01"
tags: ["obsidian", "git", "sync", "windows", "cron", "task-scheduler", "powershell"]
---
Syncing Obsidian in Windows with Windows Task Scheduler and git
@@ -1,14 +1,9 @@
+++
title = "ScriptCTF 2025 Writeup"
description = "ScriptCTF 2025 Writeup"
author = "andwati"
date = 2025-08-18
draft = false
[taxonomies]
tags = [ "ctf", "rev", "web", "forensics" ]
+++
---
title: "ScriptCTF 2025 Writeup"
description: "ScriptCTF 2025 Writeup"
date: "2025-08-18"
tags: ["ctf", "rev", "web", "forensics"]
---
![ScriptCTF mascot illustration of a wizard coding at a laptop, with the tagline "scriptCTF makes CTFs fun and approachable for all skill levels!"][image1]
Over the weekend, I participated in ScriptCTF, a capture-the-flag competition packed with diverse challenges spanning cryptography, forensics, reverse engineering, and web exploitation.
@@ -1,13 +1,9 @@
+++
title = "Self-Hosting Gitea and Automating GitHub Mirrors"
description = "Self-Hosting Gitea and Automating GitHub Mirrors (and Everything Cloudflare Broke Along the Way)"
date = 2026-08-19
draft = false
[taxonomies]
tags = [ "self-hosting", "gitea", "dokploy", "cloudflare", "github-actions", "bash" ]
+++
---
title: "Self-Hosting Gitea and Automating GitHub Mirrors"
description: "Self-Hosting Gitea and Automating GitHub Mirrors (and Everything Cloudflare Broke Along the Way)"
date: "2026-08-19"
tags: ["self-hosting", "gitea", "dokploy", "cloudflare", "github-actions", "bash"]
---
I run a Hetzner box with [Dokploy](https://docs.dokploy.com) for most of my personal infrastructure. I'd been meaning to get a self-hosted git server on it for a while, mainly so I have a durable mirror of my GitHub repos that doesn't depend on GitHub staying up, staying free, or staying interested in hosting my code. This is the log of actually doing it: what I picked, what broke, and the debugging trail for the parts that took longer than they should have.
@@ -166,9 +162,11 @@ A few decisions worth explaining.
**SSH on 2222, not 22.** The host's own sshd already owns port 22, so Gitea's internal SSH got mapped to 2222, with [`SSH_PORT`](https://docs.gitea.com/administration/config-cheat-sheet/) set to match so Gitea generates correct clone URLs.
{% <note> %}
<aside class="callout callout-note">
I later set `GITEA__server__DISABLE_SSH=true` (see [`DISABLE_SSH`](https://docs.gitea.com/administration/config-cheat-sheet/) in Gitea's config cheat sheet) and dropped the port mapping entirely. Cloudflare's proxy only forwards HTTP and HTTPS traffic, not arbitrary TCP, so SSH on 2222 was simply unreachable once `gitea.andwati.com` sat behind the orange cloud. The two ways around that were a DNS-only subdomain, which leaks the origin IP to anyone who runs `dig`, or a Cloudflare Tunnel, which is more moving parts than a personal git server's SSH clone was worth. HTTPS clone already worked fine through the proxy, so disabling SSH was the simpler trade.
{% </note> %}
</aside>
**Traefik/Dokploy terminates TLS, Gitea speaks plain HTTP internally.** [Dokploy's domain manager](https://docs.dokploy.com/docs/core/domains) handles the Let's Encrypt cert and routes to the container on port 3000. `REVERSE_PROXY_TRUSTED_PROXIES` is set so Gitea trusts the forwarded headers instead of thinking every request comes from the internal Docker network.
@@ -180,13 +178,16 @@ I later set `GITEA__server__DISABLE_SSH=true` (see [`DISABLE_SSH`](https://docs.
Two tokens drive the mirror script, and both need to be scoped correctly or the migrate call fails in ways that look unrelated to permissions.
### **GitHub token (`GH_PAT`).**
### **GitHub token (`GH_PAT`).**
A classic Personal Access Token with the `repo` scope, generated at [github.com/settings/tokens](https://github.com/settings/tokens). This is the scope GitHub's own docs point to for reading private repositories, and it's also what Gitea's own migration guide recommends when the source is GitHub. A fine-grained token works too, provided it has read access to Contents and Metadata across all the repos you want mirrored, but classic `repo` is simpler to reason about for a personal account with dozens of repos rather than picking them one by one.
### **Gitea token (`GITEA_TOKEN`).**
### **Gitea token (`GITEA_TOKEN`).**
Needs the `write:repository` scope, generated from your Gitea instance at Settings → Applications → Generate New Token (`https://<your-gitea-domain>/user/settings/applications`). `write:repository` covers both the `/repos/migrate` call that creates new mirrors and the `PATCH` call the script uses to sync visibility on existing ones. `read:repository` alone isn't enough since both of those are write operations.
### **Where they get set.**
### **Where they get set.**
Both live as encrypted secrets on the `gitea-mirror-sync` repo, not in the script or the workflow file: Settings → Secrets and variables → Actions → New repository secret, alongside `GITEA_URL` and `GITEA_OWNER`. The workflow references them only as `${{ secrets.GH_PAT }}` and `${{ secrets.GITEA_TOKEN }}`, so neither value is ever visible in a log or checked into the repo itself.
## Bot Fight Mode: the wall you can't route around
@@ -1,20 +1,15 @@
+++
title = "Semantic Versioning(SemVer) and Conventional Commits"
description = "Adopting best practices in your git and build workflows."
author = "andwati"
date = 2023-01-24
draft = false
[taxonomies]
tags = [ "git", "semver", "workflow", "conventional-commits" ]
+++
---
title: "Semantic Versioning(SemVer) and Conventional Commits"
description: "Adopting best practices in your git and build workflows."
date: "2023-01-24"
tags: ["git", "semver", "workflow", "conventional-commits"]
---
Adopting best practices in your git and build workflows.
# What is Semver?
Semantic versioning or just SemVer is a dependency naming guidline that aims to bring some sanity into naming libraries and packages in software development. It is a standard versioning system created by **Tom Preston Werner** (co-founder of Github) that’s used to communicate what changes are in a release.
Semantic versioning or just SemVer is a dependency naming guideline that aims to bring some sanity into naming libraries and packages in software development. It is a standard versioning system created by **Tom Preston Werner** (co-founder of Github) that’s used to communicate what changes are in a release.
An efficient and smooth versioning becomes crucial for efficient software delivery expecially for projects that expose a public API. This saves the developers the chaos that is **dependency hell**.Dependency hell is a colloquial term for the frustration of some software users who have installed software packages which have dependencies on specific versions of other software packages.
@@ -1,14 +1,9 @@
+++
title = "Subresource Integrity"
description = "Understanding Subresource Integrity"
author = "andwati"
date = 2023-04-29
draft = false
[taxonomies]
tags = [ "subresource-integrity", "csp", "cors" ]
+++
---
title: "Subresource Integrity"
description: "Understanding Subresource Integrity"
date: "2023-04-29"
tags: ["subresource-integrity", "csp", "cors"]
---
Understanding Subresource Integrity
@@ -1,13 +1,9 @@
+++
title = "The Single Byte That Kills Your Exploit: Understanding Endianness"
description = "Problem: Computers speak different languages, like people"
date = 2025-11-09
draft = false
[taxonomies]
tags = [ "blog" ]
+++
---
title: "The Single Byte That Kills Your Exploit: Understanding Endianness"
description: "Problem: Computers speak different languages, like people"
date: "2025-11-09"
tags: ["blog"]
---
If you’ve ever crafted a perfect shellcode and ROP chain only to have your exploit immediately crash with a SIGSEGV(a signal sent by the operating system to a program when it attempts to access a protected or invalid memory location) or EIP(a 32-bit CPU register in the x86 architecture that holds the memory address of the next machine instruction to be executed) pointing to garbage, you’ve likely met the silent killer of beginners: Endianness.
@@ -1,14 +1,9 @@
+++
title = "What say you?"
description = "Adding a comment system by utterances"
author = "andwati"
date = 2023-08-24
draft = false
[taxonomies]
tags = [ "blog", "utterances" ]
+++
---
title: "What say you?"
description: "Adding a comment system by utterances"
date: "2023-08-24"
tags: ["blog", "utterances"]
---
Free speech enhanced
@@ -1,19 +1,14 @@
+++
title = "Configure Vue.js app With Tailwind CSS"
description = "Tailwindcss in vuejs "
author = "andwati"
date = 2021-06-12
draft = false
[taxonomies]
tags = [ "javascript", "vuejs", "tailwindcss" ]
+++
---
title: "Configure Vue.js app With Tailwind CSS"
description: "Tailwindcss in vuejs "
date: "2021-06-12"
tags: ["javascript", "vuejs", "tailwindcss"]
---
A guide on how to use [Tailwind CSS](https://tailwindcss.com/) in your Vue project.
> **Note**
> This article was originaly published on my old medium blog. I moved it here for archival purposes.
> This article was originally published on my old medium blog. I moved it here for archival purposes.
Tailwind is a utility-first CSS framework for rapidly building custom user interfaces. It is an un-opinionated framework which means that it doesn't come with custom configurations like Bootstrap or Bulma(Yeah, I know, no one should tell you how to live your life).
@@ -1,13 +1,9 @@
+++
title = "What is pwn?"
description = "No not that one"
date = 2025-09-03
draft = false
[taxonomies]
tags = [ "blog" ]
+++
---
title: "What is pwn?"
description: "No not that one"
date: "2025-09-03"
tags: ["blog"]
---
> Binary Exploitation is a broad topic within Cyber Security which really comes down to finding a vulnerability in the program and exploiting it to gain control of a shell or modifying the program's functions ~ctf101
@@ -1,14 +1,9 @@
+++
title = "The One Thing Nobody Tells You About WiFi Hotspots on Arch Linux"
description = "A minimal Arch install is great until NetworkManager silently fails to find a binary you didn't know you needed"
author = "andwati"
date = 2026-04-16
draft = false
[taxonomies]
tags = [ "arch", "linux", "networking", "kde" ]
+++
---
title: "The One Thing Nobody Tells You About WiFi Hotspots on Arch Linux"
description: "A minimal Arch install is great until NetworkManager silently fails to find a binary you didn't know you needed"
date: "2026-04-16"
tags: ["arch", "linux", "networking", "kde"]
---
I spent longer than I'd like to admit trying to get a simple WiFi hotspot running on Arch. Not because it's hard — it genuinely isn't — but because of one missing piece that the NetworkManager documentation buries so deep you'd think it was classified.
-17
View File
@@ -1,17 +0,0 @@
+++
title = "File over app"
description = "Take control of your artifacts"
author = "andwati"
date = 2023-12-18
draft = true
[taxonomies]
tags = [ "obsidian", "entropy", "notes" ]
+++
Take control of your artifacts
In my endless endevours to find the perfect note taking app led me to obsidian, a note taking software that uses markdown files as the file format. All software is ephemereal in nature and yoour artifacts need to survive the decline of your favorite app. The files you create should be available to you and shouldt rrequire specialized software to access.
Your data shouldt be locked behind a paywall or an authentication system that is not under your control.