DEV Community

Victor B Vieira
Victor B Vieira

Posted on

Anonymity Is Not a Checkbox: Lessons From Building a Whistleblowing System

When we started building the whistleblowing channel at Sigilo Profissional, anonymity seemed like one of the simpler requirements.

Do not ask for a name.

Do not require an email address.

Do not force users to create an account.

Done, right?

Not exactly.

The deeper we went into the architecture, the clearer it became that anonymity is not a UI feature. It is a system-wide constraint.

Here are some of the problems we had to think about.


1. A Form Without a Name Can Still Identify Someone

A reporting page can display:

"Your report is 100% anonymous."

But the infrastructure behind it may still collect:

  • IP addresses
  • X-Forwarded-For
  • session identifiers
  • persistent cookies
  • User-Agent data
  • infrastructure logs
  • precise timestamps
  • analytics events

The problem is not always one individual data point.

It is correlation.

A report submitted at 14:37, for example, may become much less anonymous if someone can compare that timestamp with corporate VPN, Wi-Fi, firewall, or internal access logs.

This led us to a simple privacy principle:

If identifiable data is not required to provide the feature, why store it?

At Sigilo, whistleblowers do not need to create an account, provide a name, or enter an email address.

We also do not store their IP address.

Instead, reports can be accessed later through an independently generated protocol.

Which creates another interesting problem.


2. Continuity Does Not Require Identity

After submitting a report, the whistleblower may need to return later.

Maybe they want to check the investigation status.

Maybe the compliance team needs additional information.

Maybe they need to upload another document.

In a traditional application, the solution would probably be:

email + password
Enter fullscreen mode Exit fullscreen mode

or a persistent authenticated session.

But the question we actually need to answer is not:

"Who is this user?"

It is:

"Is this person authorized to access this report?"

Those are different problems.

We can preserve continuity without establishing real-world identity.

Conceptually:

anonymous user
      ↓
random protocol
      ↓
specific report
      ↓
follow-up communication
Enter fullscreen mode Exit fullscreen mode

The system knows that someone has the correct credentials to access a specific case.

It does not need to know who that person is.

That distinction between identity and continuity became one of the most interesting patterns in the product.


3. Uploaded Files Are Part of the Threat Model

Removing IP logging still does not make a system anonymous by itself.

Suppose someone uploads a .docx.

That file may contain:

  • author information
  • operating-system usernames
  • creation dates
  • software metadata
  • revision history

Images may contain EXIF metadata.

Spreadsheets can contain document properties.

Even screenshots may accidentally expose information the sender did not intend to share.

So the threat model cannot stop at the HTTP request.

User-generated files are part of the anonymity boundary too.

And there is another limitation no architecture can completely solve.

Imagine a report containing:

"Yesterday at 2 PM, during a meeting attended only by my manager, John, and me..."

The application can minimize technical traces.

It cannot prevent contextual inference.

Privacy engineering can reduce what the system leaks.

It cannot remove information voluntarily provided by the human.


4. Conflict of Interest Becomes an Authorization Problem

Another requirement that initially looked like a business rule quickly became an access-control problem.

Imagine three managers responsible for reviewing reports.

The first version of the workflow looks simple:

Report
   ↓
Responsible Group
Enter fullscreen mode Exit fullscreen mode

Now imagine the report is about one of those three managers.

That person should obviously not receive access to the case.

So the workflow becomes closer to:

Report
   ↓
Responsible Group
   ↓
Detect involved reviewers
   ↓
Exclude them from the case
   ↓
Fallback route if necessary
Enter fullscreen mode Exit fullscreen mode

In simplified pseudocode:

def can_access_report(reviewer, report):
    if reviewer.id in report.involved_people:
        return False

    return reviewer.id in report.assigned_reviewers
Enter fullscreen mode Exit fullscreen mode

But then more questions appear.

What if every reviewer is involved?

What if a reviewer joins the group after the case was created?

Should access be retroactive?

What happens when someone leaves the investigation team?

How do we preserve an audit trail for every access change?

A seemingly simple workflow quickly becomes a problem involving authorization, auditability, segregation of duties, and conflict-of-interest management.


5. Technical Anonymity Is Not Enough

This is probably the part I find most interesting.

We can strip IP addresses.

Minimize logs.

Use strong access controls.

Avoid persistent identifiers.

Protect tenant boundaries.

But none of that automatically answers this question:

Why should the employee believe us?

The person using the system may be reporting their own manager.

Harassment.

Fraud.

Discrimination.

Something that could affect their career.

In that context, "Trust us, your data is safe" is not enough.

Some technical decisions become part of the product experience itself.

For example:

"We do not store your IP address."

That is an infrastructure decision.

But it is also information that helps the whistleblower understand why the system is designed to protect their anonymity.

This changed how I think about privacy-sensitive software.

The user's perception of safety is not separate from the architecture.

It is part of whether the product works at all.


💬 Community Question: When Is a System Truly Anonymous?

The biggest lesson for me has been that anonymity is not something you add at the end of development.

It affects infrastructure, data collection, authentication, authorization, file handling, UX, and even internal business processes.

So I would love to hear how other developers approach this:

At what point do you think a software product can legitimately describe itself as anonymous?

And which layer of the stack do you consider the hardest to protect against indirect identification?

Especially interested in edge cases from people working with privacy, security, or other sensitive-data systems.


Victor Vieira | CTO & Co-founder @ Sigilo Profissional

🔗 LinkedIn: Victor Vieira

Top comments (0)