This summer, I had the incredible opportunity to work with the Mobian project for Google Summer of Code 2025. My goal was to continue the work on tweakster to a point where it could integrate smoothly with the Mobian ecosystem. This post is a summary of my work, the challenges I faced, and what’s next for tweakster.

The Root Problem

Since the start of Mobian in 2020, the goal has always been clear: have one bootable image for all kinds of non-x86 devices. That’s much harder than it sounds. There are subtle but crucial hardware differences across devices that require specific tweaks, making the mission of achieving a Universal Kernel on a system level very difficult.

Another pain point is the significant number of new non-x86 Linux-capable devices that have hit the market in the past few years, especially ARM64 laptops, mobile phones, and tablets. Maintaining a device-specific package for each one is impractical. It would take us far away from the “universal system” dream and make Mobian devs’ jobs even harder!

How It All Started

After a lot of thought from the team and my mentor, Arnaud (@a-wai), they came up with tweakster, the hero of our story. Simply put, tweakster is a system daemon that identifies the device it’s running on by checking the device-tree compatible property. It then determines the necessary tweaks by reading a pre-prepared config file, processes tweaks files, then creates symlinks to the resulting files into the system. The first attempt in 2022, packaged for Debian, saw some improvements over the years, leading to its latest form in 2024.

A Change in Plans

It turned out the first attempt was a bit over-engineered. It needed a complete refactoring, following the philosophy of starting simple and growing methodically. This is where I came in. Working hand-in-hand with my mentors, Arnaud (@a-wai) and Jarrah (@Undef), we forged the new vision of tweakster, so it will eventually make it easier to ship Mobian to new devices.

The new approach is simple: tweakster still performs device identification, but then it leverages Debian’s mature packaging system. It reads the device-specific config (using a completely new file format), copies the required tweaks into a newly created Debian package, and then builds and installs that package, cleaning temporary files afterwards. It is meant to be run as a systemd service, activated only on the first boot right after the kernel is running and the root filesystem is mounted. Once its job is done, it exits gracefully and rests forever.

Disasters All Around

That was the plan, anyway. But for someone coming from Windows to Ubuntu just a few weeks before GSoC (out of pure curiosity), setting up the development environment was a nightmare.

Dealing with Debian packaging tools in Ubuntu was a bit messy, especially dch, which for some reason was acting weirdly on VS Code (didn’t know about setting DEBFULLNAME, DEBEMAIL, EDITOR,… back then). I decided to shift to Debian Bookworm (the stable release at the time). Then I had the cool idea to develop purely in the terminal and throw VS Code in the trash. That solved the dch problem (temporarily), though I still had to set DEB* attributes anyway. But then I decided to go further: why not use a container to avoid messing up my system with development packages and keep everything tight?

Docker wasn’t for me, but I found out about LXC and got really excited. It turned out to be much harder than I thought. Setting up LXC network config and system-wide UID, GID mappings was a pain. Even user-level configuration was tricky, and permission problems were everywhere. After four days of wrestling with it, it finally worked!

But why stop there? The vim-memes are famous and the coolest engineers I knew at the time were using Vim/Neovim. So, I gave it a shot. Using a pre-configured Neovim setup wasn’t for me. I like to know my stuff deeply. I cloned the Neovim repo, built it from source, and configured it manually, learning the basics of Lua along the way. It was a struggle to get what was in my head onto the screen. I broke my config nearly 10 times (maybe even more!) before getting something I’m happy with. This detour took a full week.

Then came the main challenge: I had never written a line of Rust before. Coming from the C family, Rust’s syntax looked alien to me, but I had no choice: I had to learn it and get to work. And let’s not forget learning how to set up SSH and GPG keys for my very first commit and merge request. That was fun to learn!

The Main Course

All of that happened before GSoC coding officially started! During the coding period, with the incredible help of my mentors, I managed to get some cool stuff done.

In the tweakster repo:

  • Replace hardcoded tweaks path: A fun refactor using env vars to dynamically get the tweaks location. [merged]
  • Remove obsolete modules and introduce new config format: This was the main entry point for tweakster’s new vision, removing all old Rust modules and symlink logic, making way for the new config format. [merged]
  • Add file processing module: A module to handle all file operations from reading and parsing config to creating a list of package-related file paths. [under review]
  • Add packaging module: This piece takes the list of files generated by the file processing module and proceeds to create a package, copy files into it, build it, and finally install it on the system. [under review]
  • Packaging tweakster: An attempt to package tweakster itself as a Debian package, but it turned out there was a better way to do it. [closed]

In the tweaks-data repo (where all tweaks and device-configs live):

Is It in Its Finest Form?

Assuming the work under review gets merged, the current state of tweakster and tweaks-data is functional. But there is plenty of room for improvement! For example:

  • Improve the test suite by adding test cases for all new functionality.
  • Make tweakster set DEBFULLNAME to avoid using root as maintainer name when generating the package.
  • Improve error handling throughout the codebase.
  • Improve the current state of config validation to avoid eliminating files that hit the same dest but with different purposes.
  • Add a CLI option (–config-validate) for a dry run.
  • Add fallback functionality for tweakster to use common tweaks if no device-specific config is found.
  • Improve the README and documentation to help new contributors.
  • Package tweakster for Debian using debcargo-conf.
  • Make tweakster run on each boot and create a new version of the installed tweaks’ package if the configs and/or tweaks files have been updated.

…and even MORE! Anyone is more than welcome to contribute. Just drop a “Hi!” on #mobian-dev:matrix.org and people will happily help you get started, me included ;)

That’s It? That’s All You Got?

Besides all the technical stuff, I learned some soft skills I never would have otherwise. The most important lesson was learning to enjoy the process. During the first half of the journey, I was unnecessarily stressing myself out over nothing. That changed dramatically after I figured out that it’s totally fine to not know everything and that I was actually doing well. My mentors helped a lot with that, too.

A Round of Applause

I am incredibly thankful and grateful to my mentors for believing in me and giving me this opportunity to thrive.

A huge thank you to Jarrah (@Undef) for co-mentoring me and always being there. He helped me when I was stuck, offered fresh ideas, reviewed my code thoughtfully, and was always responsive in chat or meetings, no matter how naive my questions were.

And most especially, thank you to Arnaud Ferraris (@a-wai) for everything: for being there from the very beginning, laying the foundation of my understanding of tweakster and the Mobian project, how to communicate effectively, introducing me to the team, and helping me grow. He gave me the space and confidence to make mistakes and recover, and, most importantly, the courage to dig deeper into the world of Mobile Linux.