MarkdownAtlas: A Privacy-First Markdown Viewer for macOS

MarkdownAtlas: A Privacy-First Markdown Viewer for macOS

I recently built MarkdownAtlas, a markdown viewer for macOS that prioritizes privacy above everything else. No telemetry, no internet connection, no tracking—just you and your markdown files.

The Problem

Most modern markdown viewers come with baggage:

I wanted something different: a tool I could trust, that respects my privacy, and that I can actually audit.

The Solution

MarkdownAtlas is built from scratch using native macOS technologies:

Privacy by Design

Privacy isn’t a feature you bolt on—it’s a fundamental architectural choice. Here’s how MarkdownAtlas achieves it:

  1. No network code - The app literally has zero networking code. It can’t phone home because that functionality doesn’t exist.

  2. Auditable - At under 1000 lines of code, you can actually read and understand the entire codebase in an afternoon. No hidden surprises.

  3. Zero dependencies - Every dependency is a trust boundary. By having none, there’s no supply chain to compromise.

  4. Native only - Uses only Apple’s built-in frameworks that ship with macOS. No external binaries, no JavaScript runtime, no bundled interpreters.

MarkdownAtlas

Technical Approach

Building a markdown viewer in under 1000 lines required some careful decisions:

Rendering Engine

Instead of embedding a web view or markdown library, I built a simple parser using NSAttributedString. It handles:

The rendering happens in ~200 lines of code, converting markdown text directly to native macOS rich text.

File Navigation

The sidebar uses NSOutlineView for hierarchical folder browsing. I initially tried NSTableView but ran into issues with file hierarchy. Switching to NSOutlineView gave native tree visualization for free.

State Management

Navigation history is managed with simple arrays:

@property (strong) NSMutableArray *navigationHistory;
@property (strong) NSMutableArray *scrollPositions;
@property NSInteger navigationIndex;

Back/forward buttons work just like a browser, preserving scroll position when you navigate back to a previously viewed file.

Icon Generation

Even the icon generation avoids dependencies. Instead of Python + Pillow (my original approach), I use only macOS built-in tools:

# icons/generate_icon.sh
sips -s format png -z 1024 1024 icon.svg --out icon_1024.png
iconutil -c icns AppIcon.iconset

The SVG icon features a folder with tree structure lines and “MD” text, clearly communicating what the app does.

Why Native Matters

Using native APIs instead of cross-platform frameworks gave me:

Performance: The app launches instantly and renders markdown in real-time as you type. No JavaScript overhead, no electron bloat.

Size: The compiled binary is ~100KB. An equivalent Electron app would be 100MB+.

Trust: Apple’s frameworks are audited, sandboxed, and come with macOS. No third-party code to trust.

Integration: Native macOS look and feel. The app respects system preferences for dark mode, fonts, and window management.

The Zero Dependencies Philosophy

Every dependency is a liability:

By staying at zero dependencies, MarkdownAtlas eliminates all of these concerns. The only code that runs is code I wrote or that ships with macOS.

Challenges

Unicode in Tables

The hardest bug was table rendering mangling Unicode characters. Stars and checkmarks displayed as ✓è,≠è garbage.

The problem? Using UTF8String with C-style formatting:

// ❌ Broken - corrupts Unicode
[rowText appendFormat:@"%-*s", (int)width, [cell UTF8String]];

// ✅ Fixed - preserves Unicode
[rowText appendString:cell];
for (NSInteger p = 0; p < padding; p++) {
    [rowText appendString:@" "];
}

Code Block Backgrounds

Initially only the ``` fence lines had gray backgrounds, not the content between them. Fixed by adding state tracking:

BOOL inCodeBlock = NO;
// ...
if ([line hasPrefix:@"```"]) {
    inCodeBlock = !inCodeBlock;
} else if (inCodeBlock) {
    // Apply code styling
}

macOS Gatekeeper

Since I’m not paying Apple $99/year for a developer certificate, users see scary warnings when first opening the app. The solution is clear documentation:

I’d rather document this than compromise on privacy or pay Apple’s gatekeeping tax.

Build System

The entire build is a simple Makefile:

CC = gcc
CFLAGS = -std=c2x -Wall -Wextra
FRAMEWORKS = -framework Cocoa

build: $(APP_BUNDLE)
    $(CC) $(CFLAGS) $(FRAMEWORKS) -o $@ main.m

No webpack, no babel, no package.json with 500 dependencies. Just make and you’re done.

What I Learned

  1. Native development is underrated - The barrier to entry is higher, but the payoff in performance, size, and trust is massive.

  2. Constraints breed creativity - The “under 1000 lines” goal forced me to simplify and avoid over-engineering.

  3. Privacy by default is possible - You don’t need telemetry. You don’t need analytics. These are choices, not requirements.

  4. Documentation matters - Because the app is unusual (native, unsigned, minimal), good documentation is critical for user trust.

  5. Simple tools can be powerful - Markdown viewing doesn’t need AI, cloud sync, or collaboration features. Sometimes a simple, focused tool is exactly what you need.

Open Source

MarkdownAtlas is fully open source on GitHub: stan-kondrat/MarkdownAtlas

The entire codebase is auditable. No minified JavaScript, no obfuscated binaries—just readable Objective-C that does exactly what it says.

Future

I’m keeping the scope intentionally limited. No plans for:

The goal is a tool that does one thing well and respects your privacy. Adding features would compromise that mission.

Try It

If you’re on macOS and care about privacy, give it a try:

git clone https://github.com/stan-kondrat/MarkdownAtlas.git
cd MarkdownAtlas
make
open build/MarkdownAtlas.app

Or download a release from GitHub and remember to bypass Gatekeeper on first run.


In a world where everything phones home, collects telemetry, and mines your data, it’s refreshing to build something that simply doesn’t. MarkdownAtlas is my small contribution to a more privacy-respecting future.

Your files are yours. Your data is yours. Software should respect that.