<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Victor B Vieira</title>
    <description>The latest articles on DEV Community by Victor B Vieira (@bidu).</description>
    <link>https://dev.to/bidu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4125001%2Ff29b66c5-b49e-4ab3-a5b1-b28e1bfb4e8c.png</url>
      <title>DEV Community: Victor B Vieira</title>
      <link>https://dev.to/bidu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bidu"/>
    <language>en</language>
    <item>
      <title>When a Legal Requirement Turns Into an Authorization Problem</title>
      <dc:creator>Victor B Vieira</dc:creator>
      <pubDate>Fri, 02 Oct 2026 17:06:00 +0000</pubDate>
      <link>https://dev.to/bidu/when-a-legal-requirement-turns-into-an-authorization-problem-37c</link>
      <guid>https://dev.to/bidu/when-a-legal-requirement-turns-into-an-authorization-problem-37c</guid>
      <description>&lt;p&gt;At first, the requirement sounded almost trivial:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Employees need a way to submit anonymous reports.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Build a form. Store the report. Give HR an admin panel.&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;Except it wasn't.&lt;/p&gt;

&lt;p&gt;Once we started breaking the requirement down, a simple reporting form became an authorization problem, then a privacy problem, then a governance problem.&lt;/p&gt;

&lt;p&gt;And most of the difficult parts had very little to do with the form itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The requirement
&lt;/h2&gt;

&lt;p&gt;In Brazil, Law No. 14,457/2022 introduced several requirements for companies that maintain a CIPA — the country's Internal Commission for Accident and Harassment Prevention.&lt;/p&gt;

&lt;p&gt;One of them is particularly interesting from a software perspective.&lt;/p&gt;

&lt;p&gt;Companies need procedures for receiving and following up on reports, investigating what happened, and potentially applying sanctions while &lt;strong&gt;guaranteeing the anonymity of the person submitting the report&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's a legal requirement.&lt;/p&gt;

&lt;p&gt;But someone eventually has to turn it into software.&lt;/p&gt;

&lt;p&gt;And that's where the ambiguity starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is allowed to see a report?
&lt;/h2&gt;

&lt;p&gt;Imagine the first version of the application.&lt;/p&gt;

&lt;p&gt;You have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a public reporting form;&lt;/li&gt;
&lt;li&gt;an authenticated dashboard;&lt;/li&gt;
&lt;li&gt;attachments;&lt;/li&gt;
&lt;li&gt;comments;&lt;/li&gt;
&lt;li&gt;report statuses;&lt;/li&gt;
&lt;li&gt;an audit history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pretty standard internal software.&lt;/p&gt;

&lt;p&gt;Then this report arrives:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My manager has been harassing me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now suppose that manager also has administrative access to the reporting system.&lt;/p&gt;

&lt;p&gt;Suddenly, the permission model is wrong.&lt;/p&gt;

&lt;p&gt;It doesn't matter that the reporter's name is hidden if the person being reported can open the case, see its contents, or infer where it came from.&lt;/p&gt;

&lt;p&gt;So we get a new rule:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Someone mentioned in a report should not be able to manage that report.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That sounds obvious after you hear it.&lt;/p&gt;

&lt;p&gt;It isn't necessarily obvious when you design the first database schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;admin&lt;/code&gt; stops being useful pretty quickly
&lt;/h2&gt;

&lt;p&gt;A lot of internal software begins with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user
admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this kind of system, that abstraction falls apart almost immediately.&lt;/p&gt;

&lt;p&gt;You might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HR;&lt;/li&gt;
&lt;li&gt;compliance;&lt;/li&gt;
&lt;li&gt;company directors;&lt;/li&gt;
&lt;li&gt;external investigators;&lt;/li&gt;
&lt;li&gt;organization administrators;&lt;/li&gt;
&lt;li&gt;members of the CIPA.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different roles can see different things.&lt;/p&gt;

&lt;p&gt;But even that isn't enough.&lt;/p&gt;

&lt;p&gt;Because access can depend on the &lt;strong&gt;content of the report itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Alice may normally be allowed to see every report.&lt;/p&gt;

&lt;p&gt;Unless Alice is mentioned in this one.&lt;/p&gt;

&lt;p&gt;So authorization becomes something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;can_access =
    has_permission
    &amp;amp;&amp;amp; !is_involved_in_report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which means this is no longer just classic RBAC.&lt;/p&gt;

&lt;p&gt;The authorization decision now depends on context.&lt;/p&gt;

&lt;p&gt;And once you go down that path, a lot of other questions show up.&lt;/p&gt;

&lt;p&gt;Who determines that someone is involved?&lt;/p&gt;

&lt;p&gt;Can another administrator override the restriction?&lt;/p&gt;

&lt;p&gt;What happens if every person with the appropriate role is mentioned?&lt;/p&gt;

&lt;p&gt;Who can see that access was revoked?&lt;/p&gt;

&lt;p&gt;Should the database enforce this, or should the application?&lt;/p&gt;

&lt;p&gt;A requirement that originally sounded like "add an anonymous form" is now shaping the authorization model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anonymous to whom?
&lt;/h2&gt;

&lt;p&gt;"Anonymous" is another word that sounds simple until you have to define it.&lt;/p&gt;

&lt;p&gt;The form not asking for a name is one thing.&lt;/p&gt;

&lt;p&gt;The company administrator being unable to identify the reporter is another.&lt;/p&gt;

&lt;p&gt;The infrastructure itself not retaining data that could later be correlated with that person is something else entirely.&lt;/p&gt;

&lt;p&gt;A perfectly anonymous-looking form can still sit behind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IP logs;&lt;/li&gt;
&lt;li&gt;analytics scripts;&lt;/li&gt;
&lt;li&gt;authentication middleware;&lt;/li&gt;
&lt;li&gt;session identifiers;&lt;/li&gt;
&lt;li&gt;reverse proxy logs;&lt;/li&gt;
&lt;li&gt;third-party monitoring tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those systems are inherently bad.&lt;/p&gt;

&lt;p&gt;Most are things we would add to a normal application without thinking much about them.&lt;/p&gt;

&lt;p&gt;But privacy-sensitive products force you to ask a different question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do we actually need to collect this information?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes the safest data is simply data you never stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then comes the messaging problem
&lt;/h2&gt;

&lt;p&gt;Reports are rarely complete.&lt;/p&gt;

&lt;p&gt;Someone submits something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My manager threatened me after the meeting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For an investigation, that may not be enough.&lt;/p&gt;

&lt;p&gt;Which meeting?&lt;/p&gt;

&lt;p&gt;When?&lt;/p&gt;

&lt;p&gt;Was anyone else there?&lt;/p&gt;

&lt;p&gt;Is there a message or document related to it?&lt;/p&gt;

&lt;p&gt;Normally, the obvious solution is:&lt;/p&gt;

&lt;p&gt;Send the user an email.&lt;/p&gt;

&lt;p&gt;Except you don't know who the user is.&lt;/p&gt;

&lt;p&gt;And ideally you don't want to know.&lt;/p&gt;

&lt;p&gt;So the identity model needs to change.&lt;/p&gt;

&lt;p&gt;Instead of attaching the conversation to a person, you can attach it to the report itself.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;report_id
access_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reporter keeps a credential.&lt;/p&gt;

&lt;p&gt;They can return later, see replies, provide additional information, and follow the status of the case.&lt;/p&gt;

&lt;p&gt;The investigator talks to the holder of that credential without knowing who that person is.&lt;/p&gt;

&lt;p&gt;Nothing particularly exotic is happening technically.&lt;/p&gt;

&lt;p&gt;But a domain requirement completely changes how a very ordinary feature — messaging — has to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust is another part of the architecture
&lt;/h2&gt;

&lt;p&gt;There's also a problem code alone can't solve.&lt;/p&gt;

&lt;p&gt;You can build everything correctly.&lt;/p&gt;

&lt;p&gt;You can avoid storing IP addresses.&lt;/p&gt;

&lt;p&gt;You can minimize logs.&lt;/p&gt;

&lt;p&gt;You can encrypt sensitive fields.&lt;/p&gt;

&lt;p&gt;You can implement contextual access policies.&lt;/p&gt;

&lt;p&gt;You can automatically remove someone from a case when they're involved in it.&lt;/p&gt;

&lt;p&gt;And an employee can still look at the &lt;strong&gt;Submit report&lt;/strong&gt; button and think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this actually anonymous?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's rational.&lt;/p&gt;

&lt;p&gt;The person using the system isn't reading the source code.&lt;/p&gt;

&lt;p&gt;They're not inspecting proxy configuration.&lt;/p&gt;

&lt;p&gt;They're not reviewing database policies.&lt;/p&gt;

&lt;p&gt;They're being asked to trust the system.&lt;/p&gt;

&lt;p&gt;So technical decisions eventually become UX decisions.&lt;/p&gt;

&lt;p&gt;Compare:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your report is anonymous.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We do not ask for your identity or store your IP address.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second statement is more specific.&lt;/p&gt;

&lt;p&gt;It explains something the system actually does.&lt;/p&gt;

&lt;p&gt;That sort of explainability matters more than I originally expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The law defines an outcome, not an architecture
&lt;/h2&gt;

&lt;p&gt;This is probably the part I find most interesting.&lt;/p&gt;

&lt;p&gt;Brazilian Law No. 14,457/2022 doesn't tell developers to use PostgreSQL.&lt;/p&gt;

&lt;p&gt;It doesn't say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use row-level security
don't log IP addresses
implement contextual RBAC
require two administrators
use magic-link authentication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It defines obligations and expected outcomes.&lt;/p&gt;

&lt;p&gt;The implementation is left to the organization building the system.&lt;/p&gt;

&lt;p&gt;That happens a lot in B2B software.&lt;/p&gt;

&lt;p&gt;A requirement arrives as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We need to comply with X.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You start decomposing it.&lt;/p&gt;

&lt;p&gt;And underneath that sentence you find decisions involving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;architecture;&lt;/li&gt;
&lt;li&gt;permissions;&lt;/li&gt;
&lt;li&gt;security;&lt;/li&gt;
&lt;li&gt;privacy;&lt;/li&gt;
&lt;li&gt;data retention;&lt;/li&gt;
&lt;li&gt;UX;&lt;/li&gt;
&lt;li&gt;auditability;&lt;/li&gt;
&lt;li&gt;governance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The regulation describes the destination.&lt;/p&gt;

&lt;p&gt;Engineering has to figure out the road.&lt;/p&gt;

&lt;h2&gt;
  
  
  The form is probably the least interesting part
&lt;/h2&gt;

&lt;p&gt;This:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;textarea&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Submit report&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is easy.&lt;/p&gt;

&lt;p&gt;The hard questions come afterward.&lt;/p&gt;

&lt;p&gt;Who receives the report?&lt;/p&gt;

&lt;p&gt;Who must never receive it?&lt;/p&gt;

&lt;p&gt;Who audits access?&lt;/p&gt;

&lt;p&gt;How do you communicate with someone you can't identify?&lt;/p&gt;

&lt;p&gt;What should you log?&lt;/p&gt;

&lt;p&gt;What should you intentionally &lt;strong&gt;not&lt;/strong&gt; log?&lt;/p&gt;

&lt;p&gt;How do you handle conflicts of interest?&lt;/p&gt;

&lt;p&gt;And how do you explain all of this to the user well enough that they trust the system?&lt;/p&gt;

&lt;p&gt;That's when something that originally looked like "an HR form" becomes a much more interesting engineering problem.&lt;/p&gt;

&lt;p&gt;I'm particularly curious about one part of this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If access to a record depends on whether the current user is mentioned inside that record, where would you enforce that rule?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Application layer?&lt;/p&gt;

&lt;p&gt;Database policies?&lt;/p&gt;

&lt;p&gt;A dedicated authorization layer?&lt;/p&gt;

&lt;p&gt;Something else?&lt;/p&gt;




&lt;h3&gt;
  
  
  A note on this article
&lt;/h3&gt;

&lt;p&gt;I work as CTO and co-founder at Sigilo, where we build privacy-first reporting and organizational feedback systems.&lt;/p&gt;

&lt;p&gt;The examples and technical decisions discussed here come from problems we've encountered while designing these workflows.&lt;/p&gt;

&lt;p&gt;I used AI assistance to help structure and refine the English version of this article. The technical reasoning, product experience, and final review are my own.&lt;/p&gt;

</description>
      <category>security</category>
      <category>privacy</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Anonymity Is Not a Checkbox: Lessons From Building a Whistleblowing System</title>
      <dc:creator>Victor B Vieira</dc:creator>
      <pubDate>Thu, 24 Sep 2026 18:06:17 +0000</pubDate>
      <link>https://dev.to/bidu/anonymity-is-not-a-checkbox-lessons-from-building-a-whistleblowing-system-14em</link>
      <guid>https://dev.to/bidu/anonymity-is-not-a-checkbox-lessons-from-building-a-whistleblowing-system-14em</guid>
      <description>&lt;p&gt;When we started building the whistleblowing channel at &lt;strong&gt;Sigilo Profissional&lt;/strong&gt;, anonymity seemed like one of the simpler requirements.&lt;/p&gt;

&lt;p&gt;Do not ask for a name.&lt;/p&gt;

&lt;p&gt;Do not require an email address.&lt;/p&gt;

&lt;p&gt;Do not force users to create an account.&lt;/p&gt;

&lt;p&gt;Done, right?&lt;/p&gt;

&lt;p&gt;Not exactly.&lt;/p&gt;

&lt;p&gt;The deeper we went into the architecture, the clearer it became that &lt;strong&gt;anonymity is not a UI feature. It is a system-wide constraint.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are some of the problems we had to think about.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. A Form Without a Name Can Still Identify Someone
&lt;/h3&gt;

&lt;p&gt;A reporting page can display:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Your report is 100% anonymous."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the infrastructure behind it may still collect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IP addresses&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-Forwarded-For&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;session identifiers&lt;/li&gt;
&lt;li&gt;persistent cookies&lt;/li&gt;
&lt;li&gt;User-Agent data&lt;/li&gt;
&lt;li&gt;infrastructure logs&lt;/li&gt;
&lt;li&gt;precise timestamps&lt;/li&gt;
&lt;li&gt;analytics events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem is not always one individual data point.&lt;/p&gt;

&lt;p&gt;It is correlation.&lt;/p&gt;

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

&lt;p&gt;This led us to a simple privacy principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If identifiable data is not required to provide the feature, why store it?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At Sigilo, whistleblowers do not need to create an account, provide a name, or enter an email address.&lt;/p&gt;

&lt;p&gt;We also do not store their IP address.&lt;/p&gt;

&lt;p&gt;Instead, reports can be accessed later through an independently generated protocol.&lt;/p&gt;

&lt;p&gt;Which creates another interesting problem.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Continuity Does Not Require Identity
&lt;/h3&gt;

&lt;p&gt;After submitting a report, the whistleblower may need to return later.&lt;/p&gt;

&lt;p&gt;Maybe they want to check the investigation status.&lt;/p&gt;

&lt;p&gt;Maybe the compliance team needs additional information.&lt;/p&gt;

&lt;p&gt;Maybe they need to upload another document.&lt;/p&gt;

&lt;p&gt;In a traditional application, the solution would probably be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;email + password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or a persistent authenticated session.&lt;/p&gt;

&lt;p&gt;But the question we actually need to answer is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Who is this user?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Is this person authorized to access this report?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are different problems.&lt;/p&gt;

&lt;p&gt;We can preserve continuity without establishing real-world identity.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;anonymous user
      ↓
random protocol
      ↓
specific report
      ↓
follow-up communication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system knows that someone has the correct credentials to access a specific case.&lt;/p&gt;

&lt;p&gt;It does not need to know who that person is.&lt;/p&gt;

&lt;p&gt;That distinction between &lt;strong&gt;identity and continuity&lt;/strong&gt; became one of the most interesting patterns in the product.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Uploaded Files Are Part of the Threat Model
&lt;/h3&gt;

&lt;p&gt;Removing IP logging still does not make a system anonymous by itself.&lt;/p&gt;

&lt;p&gt;Suppose someone uploads a &lt;code&gt;.docx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That file may contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;author information&lt;/li&gt;
&lt;li&gt;operating-system usernames&lt;/li&gt;
&lt;li&gt;creation dates&lt;/li&gt;
&lt;li&gt;software metadata&lt;/li&gt;
&lt;li&gt;revision history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Images may contain EXIF metadata.&lt;/p&gt;

&lt;p&gt;Spreadsheets can contain document properties.&lt;/p&gt;

&lt;p&gt;Even screenshots may accidentally expose information the sender did not intend to share.&lt;/p&gt;

&lt;p&gt;So the threat model cannot stop at the HTTP request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User-generated files are part of the anonymity boundary too.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And there is another limitation no architecture can completely solve.&lt;/p&gt;

&lt;p&gt;Imagine a report containing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Yesterday at 2 PM, during a meeting attended only by my manager, John, and me..."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application can minimize technical traces.&lt;/p&gt;

&lt;p&gt;It cannot prevent contextual inference.&lt;/p&gt;

&lt;p&gt;Privacy engineering can reduce what the system leaks.&lt;/p&gt;

&lt;p&gt;It cannot remove information voluntarily provided by the human.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Conflict of Interest Becomes an Authorization Problem
&lt;/h3&gt;

&lt;p&gt;Another requirement that initially looked like a business rule quickly became an access-control problem.&lt;/p&gt;

&lt;p&gt;Imagine three managers responsible for reviewing reports.&lt;/p&gt;

&lt;p&gt;The first version of the workflow looks simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Report
   ↓
Responsible Group
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine the report is about one of those three managers.&lt;/p&gt;

&lt;p&gt;That person should obviously not receive access to the case.&lt;/p&gt;

&lt;p&gt;So the workflow becomes closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Report
   ↓
Responsible Group
   ↓
Detect involved reviewers
   ↓
Exclude them from the case
   ↓
Fallback route if necessary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In simplified pseudocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_access_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;involved_people&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reviewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigned_reviewers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But then more questions appear.&lt;/p&gt;

&lt;p&gt;What if every reviewer is involved?&lt;/p&gt;

&lt;p&gt;What if a reviewer joins the group after the case was created?&lt;/p&gt;

&lt;p&gt;Should access be retroactive?&lt;/p&gt;

&lt;p&gt;What happens when someone leaves the investigation team?&lt;/p&gt;

&lt;p&gt;How do we preserve an audit trail for every access change?&lt;/p&gt;

&lt;p&gt;A seemingly simple workflow quickly becomes a problem involving &lt;strong&gt;authorization, auditability, segregation of duties, and conflict-of-interest management&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Technical Anonymity Is Not Enough
&lt;/h3&gt;

&lt;p&gt;This is probably the part I find most interesting.&lt;/p&gt;

&lt;p&gt;We can strip IP addresses.&lt;/p&gt;

&lt;p&gt;Minimize logs.&lt;/p&gt;

&lt;p&gt;Use strong access controls.&lt;/p&gt;

&lt;p&gt;Avoid persistent identifiers.&lt;/p&gt;

&lt;p&gt;Protect tenant boundaries.&lt;/p&gt;

&lt;p&gt;But none of that automatically answers this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why should the employee believe us?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The person using the system may be reporting their own manager.&lt;/p&gt;

&lt;p&gt;Harassment.&lt;/p&gt;

&lt;p&gt;Fraud.&lt;/p&gt;

&lt;p&gt;Discrimination.&lt;/p&gt;

&lt;p&gt;Something that could affect their career.&lt;/p&gt;

&lt;p&gt;In that context, &lt;code&gt;"Trust us, your data is safe"&lt;/code&gt; is not enough.&lt;/p&gt;

&lt;p&gt;Some technical decisions become part of the product experience itself.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"We do not store your IP address."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is an infrastructure decision.&lt;/p&gt;

&lt;p&gt;But it is also information that helps the whistleblower understand &lt;strong&gt;why&lt;/strong&gt; the system is designed to protect their anonymity.&lt;/p&gt;

&lt;p&gt;This changed how I think about privacy-sensitive software.&lt;/p&gt;

&lt;p&gt;The user's perception of safety is not separate from the architecture.&lt;/p&gt;

&lt;p&gt;It is part of whether the product works at all.&lt;/p&gt;




&lt;h3&gt;
  
  
  💬 Community Question: When Is a System Truly Anonymous?
&lt;/h3&gt;

&lt;p&gt;The biggest lesson for me has been that anonymity is not something you add at the end of development.&lt;/p&gt;

&lt;p&gt;It affects infrastructure, data collection, authentication, authorization, file handling, UX, and even internal business processes.&lt;/p&gt;

&lt;p&gt;So I would love to hear how other developers approach this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At what point do you think a software product can legitimately describe itself as anonymous?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And which layer of the stack do you consider the hardest to protect against indirect identification?&lt;/p&gt;

&lt;p&gt;Especially interested in edge cases from people working with privacy, security, or other sensitive-data systems.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Victor Vieira&lt;/strong&gt; | CTO &amp;amp; Co-founder @ &lt;a href="https://sigiloprofissional.com.br" rel="noopener noreferrer"&gt;Sigilo Profissional&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.linkedin.com/in/victorbvieira/" rel="noopener noreferrer"&gt;LinkedIn: Victor Vieira&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>privacy</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Designing a Privacy-First Architecture for Sensitive Data: Zero-IP Logging, Postgres RLS, and AI Safety</title>
      <dc:creator>Victor B Vieira</dc:creator>
      <pubDate>Mon, 14 Sep 2026 18:32:41 +0000</pubDate>
      <link>https://dev.to/bidu/designing-a-privacy-first-architecture-for-sensitive-data-zero-ip-logging-postgres-rls-and-ai-3g23</link>
      <guid>https://dev.to/bidu/designing-a-privacy-first-architecture-for-sensitive-data-zero-ip-logging-postgres-rls-and-ai-3g23</guid>
      <description>&lt;p&gt;When building software that handles sensitive human data (like workplace feedback, satisfaction surveys, and compliance reports), security and privacy cannot be treated as optional features or mere legal disclaimers. They must be embedded into the core system design.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;Sigilo Profissional&lt;/strong&gt; (&lt;a href="https://sigiloprofissional.com.br" rel="noopener noreferrer"&gt;sigiloprofissional.com.br&lt;/a&gt;), a B2B SaaS platform focused on &lt;strong&gt;Workplace Climate Surveys, eNPS, and Whistleblowing Channels&lt;/strong&gt; compliant with &lt;strong&gt;ISO 37002&lt;/strong&gt; and &lt;strong&gt;GDPR / LGPD&lt;/strong&gt;, privacy is the fundamental prerequisite for psychological safety. If employees do not trust the platform's anonymity, engagement drops to zero.&lt;/p&gt;

&lt;p&gt;Here is a breakdown of the security architecture and privacy controls we implemented in our Python (FastAPI) and PostgreSQL stack.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Dual-Layer Multi-Tenancy (Application + PostgreSQL RLS)
&lt;/h3&gt;

&lt;p&gt;To prevent cross-tenant data leakage, we enforce tenant isolation at two independent layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Application Layer:&lt;/strong&gt; Every query filters by &lt;code&gt;firm_id&lt;/code&gt; within SQLAlchemy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Engine Layer:&lt;/strong&gt; PostgreSQL &lt;strong&gt;Row Level Security (RLS)&lt;/strong&gt; is enabled on all tenant tables. The application middleware sets &lt;code&gt;SET LOCAL app.firm_id&lt;/code&gt; per database session.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Enforcing PostgreSQL Row Level Security (RLS)&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;tenant_complaints&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;firm_isolation&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;tenant_complaints&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firm_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.firm_id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Why both?&lt;/em&gt; Even if a bug or missing filter occurs in application code, the database engine natively blocks access to data outside the active tenant context.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Zero-IP Logging Policy &amp;amp; Anonymous Protocol Tokens
&lt;/h3&gt;

&lt;p&gt;To ensure complete whistleblower anonymity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Proxy-Level IP Stripping:&lt;/strong&gt; NGINX and edge proxies strip &lt;code&gt;X-Forwarded-For&lt;/code&gt; and client IP headers. No IP addresses are saved in NGINX logs, application memory, or database records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cryptographic Tokens:&lt;/strong&gt; Anonymous reports are tracked strictly through random protocol tokens (e.g., &lt;code&gt;SGL-2026-7X3K&lt;/code&gt;). Whistleblowers can check updates using their protocol token without ever creating an account or storing session cookies.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. PII Anonymization for Responsible AI Features
&lt;/h3&gt;

&lt;p&gt;We use Large Language Models (LLMs) to assist employees in structuring clear reports and to summarize qualitative eNPS sentiment for management.&lt;/p&gt;

&lt;p&gt;To prevent sensitive Personally Identifiable Information (PII) from being sent to external AI models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests pass through an intermediate LiteLLM proxy layer that sanitizes names, emails, phone numbers, and location metadata before dispatching prompts.&lt;/li&gt;
&lt;li&gt;AI outputs are strictly used for guidance and categorization, never for autonomous decision-making.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Immutable Audit Trails
&lt;/h3&gt;

&lt;p&gt;Compliance frameworks (such as ISO 37002) require full auditability for compliance officers. We maintain immutable audit logs for administrative actions (e.g., status updates, report triage) using cryptographic hashing, ensuring complete transparency without compromising whistleblower anonymity.&lt;/p&gt;




&lt;h3&gt;
  
  
  💬 Community Question: What Are We Missing?
&lt;/h3&gt;

&lt;p&gt;As we continue scaling our engineering stack, we are reviewing our security roadmap.&lt;/p&gt;

&lt;p&gt;We would love feedback from the Dev.to community:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What automated query auditing or key rotation strategies do you recommend for multi-tenant Postgres RLS at scale?&lt;/li&gt;
&lt;li&gt;Have you implemented additional client-side payload encryption patterns that balance searchability with absolute zero-knowledge privacy?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking forward to your thoughts and suggestions in the comments!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Victor Vieira&lt;/strong&gt; | CTO &amp;amp; Co-founder @ &lt;a href="https://sigiloprofissional.com.br" rel="noopener noreferrer"&gt;Sigilo Profissional&lt;/a&gt;&lt;br&gt;
🔗 &lt;a href="https://www.linkedin.com/in/victorbvieira/" rel="noopener noreferrer"&gt;LinkedIn: Victor Vieira&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>privacy</category>
      <category>python</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
