Working on Marasi - Reflections

Posted on Nov 16, 2025

Over the past year and a half I have been hacking away at my open source project Marasi after work and on weekends. I won’t go over the details of the project, but its an open source application security testing proxy. It comes in two parts: a Marasi Library, and a GUI Application.

Dashboard screen of the Marasi GUI

It took me just over one year to release the initial open source version of both projects. After the release I took a few months break before coming in and working heavily on the project again. These past two months have been dedicated to revisiting the existing parts of Marasi, refactoring and building a strong foundation for some of the future changes and features I want to add to the application.

Project directory created on 17th May 2024

This has been one of the most enjoyable projects that I have worked on. Think of this post as a collection of my thoughts and learnings from working on the project. So feel free to jump around the sections as they do not have to be read in sequence.

Just One More Feature

You may have seen this piece of advice a few times all over the Internet. No matter how far you’ve come along, there is always “one more feature” left before you feel ready to ship. Try to avoid this line of thought at all costs, ship whatever you have and iterate from there. The value you will get from having your app in the hands of actual users - no matter how little, will easily outweigh the extra time you will spend working on an extra feature.

When I was working on Marasi, this “one more feature” was the extension system. How can I develop an extension system that will allow extensions to safely extend both the proxy and the GUI implementation?

I tried a few approaches early on, if you poke around the code hard enough you will see some of the remnants. Looking back, one of the best decisions I made was putting this on the back burner. I released Marasi with the basic core extension system, and decided to revisit this feature later. It’s been 6 months since then, and the feedback I have gotten from my colleagues was much more useful than if I had held back the release.

I won’t go into the details of some of the early issues, perhaps they deserve a separate series of posts. However, I’ll highlight one that I faced early on with the Launchpad feature.

When testing it, I was always modifying one of the query parameters or replacing a character in the post body. Naturally, that would keep the content length the same. The request gets built and looks fine, but when I actually ended up using the feature I realized I wasn’t updating the Content-Length.

In Go, when you use the ReadRequest feature, it will only read the number of bytes specified in the Content-Length header. The bug I had was when the edited request had a longer body than the original, the resulting request would have an incomplete body.

Building For Yourself Vs. Building For Others

Building one-off scripts or small tools is fun, you live with the quirks, because you are the only user. It crashes if you forget an undocumented argument, or it takes two steps instead of the one.

To build something reliable you need to invest some time, and with these tools that is something you do not have. The challenge is you will only revisit the tool when you need it, and when you need it, you don’t have the time to make it reliable. You just want to get the job done and move on.

Building something for other users is very challenging, especially when working on the UX. You need to be thoughtful on how others will use your tool, the defaults that make sense to you may not be considered sane. If you think hard enough you’ve probably used tools like this in the past, where it feels that you are fighting the tool to get things done.

This was something I noticed when I started sharing the first builds of Marasi to my colleagues, sometimes they would come back with feedback that made me take a step back and think. Yeah, it does make sense for this feature to behave like this, I don’t know why I thought about that

Slowing Down and Testing Things

I’m always excited to build out new features and try new ideas. They always seem very easy to add, just a small workaround here and there. It’s only temporary, but then you realize you lost the flexibility to adapt your code. You would lose track of all the moving parts that you were keeping track of in your head. This might be in the form of a dependency that was injected to get something to work, an extra argument that you are adding or a specific return type that you’ve specified.

When I returned to Marasi after my break, I knew that I would need to revisit the core before developing new features. It became difficult to add or modify features, especially coordinating these changes with the GUI application.

This is the point that I decided to prioritise stability, that meant breaking out packages, implementing tests and revisiting some of the earlier decisions I made throughout the project. First, I started out with some of the smaller parts, like the listener package. Then I tackled bigger components like the modifier system. My PRs have had a 1:2 - 1:3 code to test ratio during this time. While the progress felt slower, it has given me the chance to rethink some of the modules and test them in isolation.

Once I have reworked the core of Marasi, my target will be to implement an end to end test suite for the proxy itself.

Working with AI

Marasi was the first project that I worked on after the release of ChatGPT. Over time, I have experimented with these tools and adopted them when they fit. My approach is akin to a phone with notifications turned off; I have to intentionally reach out to check it. This is by design, as I treat Marasi and a lot of my other projects as an outlet. I enjoy working on them, understanding them and tinkering away over time.

The Models

My model of choice was ChatGPT up until this summer, it felt like the most “complete” product built on top of the model. I quickly became a paying customer and used it heavily for both coding and non-coding.

I tried Claude during this time and found that while it was good for coding, I didn’t want to subscribe to an additional service. I was only going to use it for coding and the limits threw me off as well.

Shortly after Gemini 2.5 released, I started experimenting with it for my daily use and was consistently impressed by the responses I was getting. My Google One subscription included 2.5 Pro access, and I eventually cancelled my ChatGPT subscription.

CLI Agents

These were one of the better “products” built on top the models. The chat-edit loop with tool use was extremely effective. During this time I tried both Claude Code and Gemini CLI, but eventually stuck with Gemini. Similar to the normal web chat, I did not want to be paying another subscription for a coding only tool.

Asynchronous Agents

This is perhaps one of my favorite products built on top of the models. I have really enjoyed using both Jules and Codex on my chat bot repositories. I upgraded core dependencies, and was able to add some new features to an otherwise stagnant code base.

I preferred this UX when interfacing with agents, and I am looking forward to how they develop in the future

My Workflow For Marasi

1) Gemini Repository Import

Code Import Feature in Gemini

Most of my coding sessions will start with this, I would import my code to Gemini and start interacting with it. This is often exploratory and I use this to propose and brainstorm changes. Depending on the objective, this activity might end up with a plan document that I would reference and re-import into new chats later on. This is especially true if I am working on something that takes a couple of days. For example, I would import the current code base along with the plan and ask Gemini for feedback on the progress. the most recent example of this was when I was working on my domain refactor or the proxy modifiers.

2) Test Cases and Documentation

Documentation is the primary use case for Gemini CLI at the moment. Once I am done with a feature or package I would ask Gemini to add some documentation.

In terms of test cases I take more of a hybrid approach, I would generate the test cases on Gemini Web. From there I would start to integrate the test case one by one. It’s an inline approach and I often get the chance to tweak or drop test cases as I go along.

3) Code Generation

This is my most inconsistent use case, my approach is similar to test case generation. Once the code repository is imported I would ask it to generate specific snippets or revise certain components. The approach is iterative and either goes really well or poorly.

When it goes bad, I found it keeps getting worse unless there is an external input to fix it. For example, when I was working on the modifiers, I needed to access the martian session methods. Gemini kept giving me methods that don’t exist. I had to find the correct documentation to reference it and “save” the chat thread.

Just Build, Don’t Worry About What Exists

I was always toying with the idea of building an application security testing proxy, but one of the reasons that kept stopping me was:

“Why build X if Y exists?

The Y here is BurpSuite and it is the best tool that you can get if you are working in application security testing. Why work on an application that will exist in the same space as a well established alternative?

Build It For Yourself

This is really the only reason you need. Don’t worry about the why, just do it, quite literally. If anything, you will come out of this having built and learnt something. This will always end up being a benefit down the line.

In my case with Marasi:

  • This was my second Golang project and was already better than my first. I picked up on many concepts that I would not have picked up on otherwise.
  • I dived deeper into proxies, the HTTP stack and other related topics that will benefit me in other areas of the field.
  • Perhaps one of my favorites was working with Lua. I finally understood why it was considered “easy to embed”. It was my first time working with it and probably will not be my last given how enjoyable it was.
  • Working with Svelte to develop the UI was fun, and with Wails it was easy to setup and begin working on a desktop GUI.

Trying Out New Ideas

Building out something is one of the better ways to try out new ideas. As a user of the existing tooling you may want to try new features or alternative approaches. Even though they might not end up working out, you will have the base to try new ideas in the future.

With Marasi I wanted to try out a few things:

Open Application File Format

One of the core goals with Marasi was to embrace open source and make it easy to extend. This started with the application file format. I opted to go for SQLite, it made the file extremely easy to work with and opened up a lot of possibilities to build on top of it. You don’t even need to use Marasi to open it.

A use case that was on my mind was the aggregate and analyse project files. Think of a reporting / archiving flow for an internal team, or a dashboard creation flow for some metrics. You can also do some cool things, for example try to ask Gemini CLI or Claude Code to read a .marasi file as a SQLite DB. It would be able to answer questions such as the most queried host. With the right tools you might be able to use this type of interaction to identify new vulnerabilities / test cases that might have been missed.

Extensions

There were a few ideas I wanted to try out with extensions, some of them ended up being more challenging than I expected. They should feel like a “first party” integration into Marasi, and working on them should feel like writing a quick python script. There are three main challenges that I will be tackling when re-visiting the extension system:

Extensions Are Part of the Project

With Marasi, the extensions are part of the project file, when you share it the same version of the extension is included. Extension settings are written / read from the project file with dedicated methods, and any request & response related data enriches the metadata column.

Very quickly I realized that I would still need to maintain the extension outside of the project, as a user when you install an extension you would expect it to be available in all your projects.

Extending Both Marasi and the GUI

By design, the extensions are running in the proxy itself. They are headless, processing requests / responses as they come in. What about the GUI? The extensions have no way of “extending” the UI, as it was running in a different context.

I am would like to solve this problem not just for my current Wails GUI, but also for any future GUI implementation built on top of Marasi. This is my next big challenge, and I have a few ideas that I want to try out to solve this.

Pipeline Bottlenecks

When I started working on the extension system, I envisioned it as a pipeline, each extension would run in order and they would build on top of each other. This would open some nice compositions, an extension might detect and flag requests with authorization and another one might act on these requests.

If the extension is doing heavy processing, it will block other requests from continuing. Adding on top of this is the network latency when an extension needs to build and send requests. There needs to be some asynchronous options for extension execution.

What’s Next?

Over the next coming months I want to focus on a few things, namely, re-visiting the extension system and “completing” it. Following that there are a few features that I would like to add:

  1. An Intruder like feature for Marasi.
  2. Revising the search feature to use FTS in SQLite
  3. Reporting features with export options and custom template support.
  4. A Marasi TUI using bubbletea
  5. Better integration for some of the embedded external tools like CyberChef