Skip to main content

Command Palette

Search for a command to run...

MINDER BLOG SERIES ( PART - 1)

Updated
10 min readView as Markdown
MINDER BLOG SERIES ( PART - 1)

Why I Chose Minder Over Everything Else

While exploring different aspects of the LFX ecosystem, I came across OpenSSF and honestly, it caught my attention.

I found it interesting that OpenSSF isn't just a single project, but an umbrella with several projects focused on improving the security of the open-source ecosystem. Even more interesting was the fact that many of these projects are closely connected to real-world software security and supply chain problems.

That immediately made me curious.

I have been building a few personal projects around security over the past few months, so somewhere along the way, I had already come across the name Minder. I couldn't remember exactly where I had heard about it, but when I started exploring OpenSSF projects more seriously, I decided to finally understand what Minder actually does.

And that's when it clicked.

Minder helps development teams and open-source communities build more secure software and provide evidence that the software they've built follows security best practices.

That idea was particularly interesting to me.

Why did Minder stand out?

For me, it wasn't just about security.

What made Minder interesting was the combination of security + backend engineering + DevSecOps.

The project has a Go codebase, and since I already have an interest in backend engineering and working with Go-based systems, contributing to Minder would give me an opportunity to work on a real production oriented codebase.

Instead of only learning security concepts theoretically, I could actually see how those concepts are implemented in software.

Things like:

  • How security policies are represented

  • How evaluations are performed

  • How APIs and backend services are structured

  • How integrations work

  • How security findings move through the system

  • How all of this fits into a larger software supply chain workflow

That was much more interesting to me than simply building another project from scratch.

And it also naturally takes me in the direction I want to explore more deeply: DevSecOps.

Building my own projects vs. contributing to Minder

For the past few months, I had mostly been learning by building my own personal projects.

That has taught me a lot.

When you're building something yourself, you control everything the architecture, the technologies, the abstractions, and even the problems you're trying to solve.

But there's another kind of learning that happens when you enter an existing production codebase.

You don't get to design everything from scratch.

You have to understand why things are already designed the way they are.

You have to read code written by people you've never met, understand existing abstractions, follow execution paths, understand tests, work with conventions, and eventually make changes without breaking what already exists.

For me, that felt like a much more valuable next step.

I wanted to move from:

“I can build my own system.”

to:

“I can understand, navigate, and contribute to someone else's production system.”

And that's a very different skill.

But there was one more question

Finding an interesting project isn't enough.

I wanted to know whether I would actually enjoy contributing to it.

So I discussed Minder with one of my friends and asked him whether he thought it would be a good project for me to pursue.

He gave me two very simple questions to answer.

1. Are the people in the community helpful?

I started exploring the community and its Slack discussions.

And the answer was yes.

I found people actively helping each other, answering questions, discussing implementation details, and being approachable specially the maintainers.

That mattered to me because when you're entering an unfamiliar codebase, the community is almost as important as the code itself.

You can read documentation for hours, but sometimes one good answer from someone who already understands the system can save you an entire day.

More importantly, interacting with maintainers and contributors is itself part of learning open source.

2. Do you actually enjoy the architecture?

This question was even more important.

My friend basically told me:

If you don't enjoy understanding how the project works, you're probably not going to enjoy contributing to it for a long time.

So I started reading the official documentation.

I went through the architecture, the different components, the concepts behind Minder, and how the pieces fit together.

And I genuinely enjoyed it.

I wasn't reading it just because I wanted to find an issue to solve.

I actually wanted to understand how the system worked.

That was probably the biggest signal for me.

The community was helpful.

The architecture was interesting.

The technology stack aligned with what I wanted to learn.

And the problem space was directly connected to security and DevSecOps.

So my friend's conclusion was pretty simple:

If both answers are yes, then go with it.

And I decided to.

From reading to actually contributing

At that point, I didn't want to keep exploring Minder only from the outside.

I wanted to see the real thing.

So I set up Minder locally and started going through the codebase.

This was the point where the journey became much more interesting.

I wanted to use the project as an opportunity to take the things I already knew especially Go, backend development, Git, and DevOps and apply them to a real production codebase while filling the gaps along the way.

That became my approach:

Don't wait until you know everything. Understand enough to make a meaningful change, then learn from the codebase, the tests, the reviews, and the people around you.

My First Serious Contribution

I didn't immediately look for the biggest or most impressive issue.I started looking for something that would force me to understand an actual part of the system.

That's how I ended up looking at the REST datasource handler.

While going through the code, I found something interesting:

// TODO: Handle fallback here.

There were already fallback and expected_status fields defined in the protobuf/API definition, but the REST datasource handler wasn't actually using them.

That immediately gave me a concrete question:

If the API already supports configuring fallbacks, what is supposed to happen when the upstream REST service returns an unexpected status code?

I traced the existing request flow before writing any code.

The important part of the flow looked roughly like:

REST DataSource Definition
          ↓
     Handler creation
          ↓
      HTTP request
          ↓
   Upstream response
          ↓
    Check status code
          ↓
   ┌──────┴────────┐
   │               │
Expected       Unexpected
status           status
   │               │
   ↓               ↓
Parse body    Check fallback
                   │
             ┌─────┴─────┐
             │           │
           Match       No match
             │           │
             ↓           ↓
       Return fallback  Existing
                         behavior

The more I understood the existing behavior, the more important one constraint became:

Don't break existing datasources.

If a datasource doesn't configure a fallback, the implementation shouldn't suddenly change how it behaves.

So instead of redesigning the response handling, I tried to make the smallest change that wired the existing configuration into the handler.

What I Actually Changed

The implementation had three main pieces.

1. Wire the existing configuration into the handler

The restHandler previously didn't retain the fallback or expected-status configuration.

I added those fields and populated them when constructing the handler:

fallback       []*minderv1.RestDataSource_Def_Fallback
expectedStatus []int32

and then:

fallback:       def.GetFallback(),
expectedStatus: def.GetExpectedStatus(),

This sounds small, but it was an important part of understanding how configuration flows through Minder.

The values already existed at the API/protobuf level. The missing piece was carrying them into the runtime handler where they could actually affect behavior.

2. Determine whether the response status is expected

I added a small helper:

func (h *restHandler) isExpectedStatus(statusCode int) bool

The behavior is intentionally simple:

  • If expected_status isn't configured, 200 OK is treated as the expected status.

  • If it is configured, the response is considered expected when its status code appears in that list.

That means a datasource can explicitly say that statuses such as 200 and 202 are valid responses.

3. Match an unexpected response against a fallback

If the upstream response has an unexpected status code, the handler checks whether a fallback has been configured for that exact status.

For example:

Upstream → 404
             ↓
      Is 404 expected?
             ↓
            No
             ↓
     Is 404 configured
       as a fallback?
          ↙       ↘
        Yes        No
         ↓          ↓
Return fallback   Keep existing
response          behavior

If a matching fallback exists, Minder returns the configured fallback body and status instead of the real upstream response.

If there isn't a matching fallback, the existing behavior remains unchanged.

That last part was important to me.

I wasn't trying to introduce a new error handling model into the REST datasource. I was implementing the missing fallback behavior while preserving the behavior that existing users already depended on.

Testing the Change

I didn't want to stop at testing the helper functions.

The behavior happens as part of an HTTP request, so I wanted to test it through the actual Call() path as well.

I added table-driven tests covering cases such as:

  • expected_status not configured → 200 is accepted

  • expected_status not configured → non 200 is unexpected

  • Explicit expected_status list matches

  • Explicit list rejects an unlisted status

  • Matching fallback is returned

  • No fallback is configured

  • Unexpected status with no matching fallback keeps the existing behavior

  • Expected status bypasses fallback handling

For the end-to-end-ish handler tests, I used Go's httptest.NewServer to simulate the upstream REST service.

That gave me a setup like:

        httptest.Server
              ↓
       mock HTTP response
              ↓
        restHandler.Call()
              ↓
       fallback handling
              ↓
          assertion

I also ran the relevant existing REST datasource tests to make sure the change didn't introduce regressions.

The implementation was formatted with gofmt, and the relevant golangci lint checks passed as well.

What This Contribution Taught Me

The actual code change wasn't enormous.

But the learning was much bigger than the diff.

This was my first experience of taking a partially implemented feature in an unfamiliar production codebase and figuring out:

  1. Where the configuration originates.

  2. How it gets propagated into the runtime.

  3. Where the actual behavior needs to change.

  4. What the existing behavior is.

  5. What behavior must remain unchanged.

  6. How to test both the isolated logic and the real execution path.

  7. How to communicate the design decision during review.

And then came the part that I think is one of the most important differences between personal projects and open source:

Code review.

I didn't just write the code and move on.

A maintainer reviewed the PR and questioned an important behavioral decision: what should happen when an unexpected status has no matching fallback?

That forced me to go back to the related issue and confirm the intended behavior rather than simply assuming my interpretation was correct.

That was a valuable lesson.

In a personal project, I can decide what the behavior should be.

In an established open source project, the code has to fit the project's existing contract and design decisions.

And that's exactly the kind of engineering experience I was hoping to get from contributing to Minder.

2 views

MINDER BLOG SERIES

Part 1 of 1

an open source platform that helps development teams and open source communities build more secure software, and prove to others that what they've built is secure.