Skip to content

Commit 3e6ee40

Browse files
committed
feat: mermaid support
1 parent 3b4e669 commit 3e6ee40

13 files changed

+2239
-739
lines changed

package-lock.json

Lines changed: 1064 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"html2canvas": "^1.4.1",
1818
"katex": "^0.16.27",
1919
"marked": "^16.4.1",
20+
"mermaid": "^11.12.2",
2021
"piml": "^1.1.1",
2122
"prismjs": "^1.30.0",
2223
"qrcode.react": "^4.2.0",
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Architecting Trust: 5 Patterns to Prevent Insider Threats
2+
3+
"Quis custodiet ipsos custodes?" — *Who watches the watchmen?*
4+
5+
In the world of software, developers are the watchmen. We hold the keys to the kingdom. We write the logic that moves money, approves loans, and deletes users. But with great power comes the potential for... well, "creative" adjustments to one's own benefit.
6+
7+
It’s not just about malicious intent. It’s about **risk**. If a single developer can modify the production database to add a zero to their bank balance without anyone noticing, your architecture has failed. "Don't do that" is not a security policy.
8+
9+
Here are 5 architectural patterns to ensure that your system is resilient to insider threats and internal fraud.
10+
11+
---
12+
13+
## 1. The Maker-Checker Pattern (The 4-Eyes Principle)
14+
15+
This is the holy grail of financial systems. The core rule is simple: **The person who initiates an action cannot be the one who approves it.**
16+
17+
### The Problem
18+
A developer writes a script to "fix" a data issue. The script also happens to credit their account with $500. They run it. Profit.
19+
20+
### The Solution
21+
Separate the **Maker** (Initiator) from the **Checker** (Approver).
22+
23+
```mermaid
24+
sequenceDiagram
25+
participant Dev as Developer (Maker)
26+
participant System as System
27+
participant Lead as Team Lead (Checker)
28+
participant DB as Database
29+
30+
Dev->>System: Submit Script (UPDATE accounts SET balance = balance + 500 WHERE id = 123)
31+
System->>System: Store script in "Pending" state
32+
System-->>Dev: Request Queued #992
33+
34+
Note over System: Script does NOT run yet.
35+
36+
Lead->>System: Review Pending Requests
37+
Lead->>System: Approve Request #992
38+
System->>DB: Execute Script
39+
System-->>Lead: Execution Complete
40+
```
41+
42+
In this architecture, the developer has the permission to `Propose`, but not to `Execute`. The Team Lead has the permission to `Approve`, but not to `Propose`. Collusion is required for fraud, doubling the difficulty.
43+
44+
---
45+
46+
## 2. Principle of Least Privilege (PoLP)
47+
48+
You’ve heard it before, but do you practice it?
49+
50+
### The Problem
51+
Your application needs to read and write to the database. So you give the application's DB user `db_owner` or `ALL PRIVILEGES`.
52+
A developer gains access to the config credentials and now has full control over the DB structure, including `DROP TABLE`.
53+
54+
### The Solution
55+
Granular permissions. The application user should only have exactly what it needs, and no more.
56+
57+
* **Application User:** `SELECT`, `INSERT`, `UPDATE` (only on specific tables). No `DELETE`? Ideally, yes (see Soft Deletes). Definitely no `DROP` or `ALTER`.
58+
* **Developer User:** `SELECT` (Read-only access to production).
59+
60+
If a developer needs to modify data, they must use a tool or API that enforces business logic (and logs it), rather than raw SQL access.
61+
62+
---
63+
64+
## 3. Event Sourcing
65+
66+
Traditional databases store the *current state*. Event Sourcing stores the *history*.
67+
68+
### The Problem
69+
If I run `UPDATE balance SET amount = 1000 WHERE user_id = 'me'`, the previous value is gone. Traces can be wiped.
70+
71+
### The Solution
72+
Don't store the state. Store the **events**.
73+
74+
Instead of a `Balance` column, you have a `Transactions` table. The balance is simply the sum of all transactions.
75+
76+
| Event ID | Timestamp | Type | Amount | User |
77+
| :--- | :--- | :--- | :--- | :--- |
78+
| 1 | 10:00 AM | Deposit | +100 | Alice |
79+
| 2 | 10:05 AM | Purchase | -20 | Alice |
80+
| 3 | 11:00 AM | Adjustment | +5000 | Alice |
81+
82+
If a "creative" developer tries to inject a +5000 adjustment, it appears as a distinct row. They cannot simply "edit" the total. To calculate the balance, the system replays the events. If that +5000 lacks a valid `Origin` (like a Payment Gateway ID), the replay logic can flag it as invalid.
83+
84+
**You cannot change the past; you can only append to the future.**
85+
86+
---
87+
88+
## 4. Immutable Audit Logs (WORM)
89+
90+
**WORM** stands for **Write Once, Read Many**.
91+
92+
### The Problem
93+
A developer changes a record and then deletes the corresponding line in the log file to cover their tracks.
94+
95+
### The Solution
96+
Ship logs immediately to a storage medium that does not support modification or deletion for a set period.
97+
98+
* **AWS S3 Object Lock:** Places a "retention period" on objects. Even the root user cannot delete them until the timer expires.
99+
* **Blockchain-like structures:** Each log entry contains a cryptographic hash of the previous entry. Modifying an old log breaks the chain, alerting the system immediately.
100+
101+
```text
102+
Log Entry N:
103+
{
104+
"timestamp": "2026-01-23T12:00:00Z",
105+
"action": "UPDATE_USER",
106+
"actor": "dev_john",
107+
"prev_hash": "a1b2c3d4..." // Hash of Entry N-1
108+
}
109+
```
110+
111+
---
112+
113+
## 5. Segregation of Duties (SoD)
114+
115+
This is the organizational counterpart to Maker-Checker.
116+
117+
### The Problem
118+
The same person writes the code, tests the code, deploys the code, and manages the database. This is common in startups ("The Full Stack Hero"), but it’s a security nightmare.
119+
120+
### The Solution
121+
Split the roles.
122+
123+
* **Dev:** Writes code.
124+
* **Ops/SRE:** Manages deployment and infrastructure.
125+
* **DBA:** Manages data integrity.
126+
127+
If a developer wants to deploy a backdoor:
128+
1. They write it.
129+
2. **Code Review:** Peer catches it.
130+
3. **CI/CD:** Automated tests run on a separate server.
131+
4. **Ops:** Deploys the artifact (the developer doesn't have SSH access to Prod).
132+
133+
By breaking the chain of custody, you ensure that no single individual has the complete keys to the kingdom.
134+
135+
---
136+
137+
### Conclusion
138+
139+
Trust is good. Architecture is better. By implementing these patterns, you protect not just the company, but the developers themselves. When the system is secure by design, no one has to look over their shoulder, wondering if they *could* break the rules. They simply *can't*.

public/posts/posts.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11
[
2+
{
3+
"slug": "architecting-trust-preventing-insider-threats",
4+
"title": "Architecting Trust: 5 Patterns to Prevent Insider Threats",
5+
"date": "2026-01-23",
6+
"updated": "2026-01-23",
7+
"description": "Developers have God-mode access, but absolute power creates absolute risk. Here are 5 architectural patterns to prevent internal fraud and system abuse.",
8+
"tags": ["architecture", "security", "design-patterns", "maker-checker", "event-sourcing", "devops"],
9+
"category": "dev",
10+
"filename": "architecting-trust-preventing-insider-threats.txt",
11+
"authors": ["fezcode"],
12+
"image": "/images/defaults/visuals-2TS23o0-pUc-unsplash.jpg"
13+
},
214
{
315
"slug": "deep-link-configuration-with-url-parameters",
416
"title": "Deep Link Configuration: Achieving a Global Parameter Observer in React",

src/components/MarkdownContent.jsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import rehypeRaw from 'rehype-raw';
66
import rehypeKatex from 'rehype-katex';
77
import { ImagesIcon } from '@phosphor-icons/react';
88
import ImageModal from './ImageModal';
9+
import MermaidDiagram from './MermaidDiagram';
910

1011
const MarkdownContent = ({ content, components = {}, className = '' }) => {
1112
const [modalData, setModalData] = useState(null);
@@ -32,8 +33,24 @@ const MarkdownContent = ({ content, components = {}, className = '' }) => {
3233
);
3334
};
3435

36+
const CodeBlock = ({ node, inline, className, children, ...props }) => {
37+
const match = /language-(\w+)/.exec(className || '');
38+
const isMermaid = match && match[1] === 'mermaid';
39+
40+
if (!inline && isMermaid) {
41+
return <MermaidDiagram chart={String(children).replace(/\n$/, '')} />;
42+
}
43+
44+
return (
45+
<code className={className} {...props}>
46+
{children}
47+
</code>
48+
);
49+
};
50+
3551
const defaultComponents = {
3652
img: CustomImage,
53+
code: CodeBlock,
3754
...components,
3855
};
3956

src/components/MermaidDiagram.jsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { useEffect, useState } from 'react';
2+
import mermaid from 'mermaid';
3+
4+
const MermaidDiagram = ({ chart }) => {
5+
const [svg, setSvg] = useState('');
6+
const [error, setError] = useState(null);
7+
8+
useEffect(() => {
9+
mermaid.initialize({
10+
startOnLoad: false,
11+
theme: 'dark',
12+
securityLevel: 'loose',
13+
fontFamily: 'monospace',
14+
});
15+
}, []);
16+
17+
useEffect(() => {
18+
const renderChart = async () => {
19+
if (!chart) return;
20+
try {
21+
const id = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
22+
// mermaid.render returns an object { svg } in newer versions
23+
const { svg } = await mermaid.render(id, chart);
24+
setSvg(svg);
25+
setError(null);
26+
} catch (err) {
27+
console.error('Mermaid render error:', err);
28+
// Mermaid might leave error text in the DOM, so we can also show a friendly message
29+
setError('Failed to render diagram. Check syntax.');
30+
}
31+
};
32+
33+
renderChart();
34+
}, [chart]);
35+
36+
if (error) {
37+
return (
38+
<div className="p-4 my-4 border border-red-500/20 bg-red-500/10 rounded text-red-400 text-sm font-mono">
39+
{error}
40+
<pre className="mt-2 text-xs opacity-50 overflow-auto">{chart}</pre>
41+
</div>
42+
);
43+
}
44+
45+
return (
46+
<div
47+
className="mermaid-container flex justify-center my-8 bg-gray-900/30 p-6 rounded-lg overflow-x-auto"
48+
dangerouslySetInnerHTML={{ __html: svg }}
49+
/>
50+
);
51+
};
52+
53+
export default MermaidDiagram;

0 commit comments

Comments
 (0)