This commit is contained in:
Bryan Ramos 2026-03-04 00:02:00 -05:00
parent 8c6b8d5d9b
commit 9576fe3e3f
21 changed files with 980 additions and 138 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.jekyll-cache
_site

View file

4
CNAME
View file

@ -1,3 +1 @@
ramos.codes ramos.codes
www.ramos.codes
bryan.ramos.codes

View file

@ -1 +1,11 @@
include: [".well-known"] title: Bryan Ramos
description: Developer / Technologist
url: "https://ramos.codes"
include:
- ".well-known"
permalink: /blog/:year/:month/:day/:title/
markdown: kramdown
highlighter: rouge

20
_includes/head.html Normal file
View file

@ -0,0 +1,20 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Theme script: runs before CSS to prevent flash of wrong theme -->
<script>
(function() {
var stored = localStorage.getItem('theme');
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.setAttribute('data-theme', stored || (prefersDark ? 'dark' : 'light'));
})();
</script>
<!-- Google Fonts: Inter -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap">
<link rel="stylesheet" href="{{ '/css/styles.css' | relative_url }}">
<script src="https://kit.fontawesome.com/f26d369dc4.js" crossorigin="anonymous"></script>

34
_includes/header.html Normal file
View file

@ -0,0 +1,34 @@
<header>
{% unless page.url == '/' %}
<a href="{{ '/' | relative_url }}" class="home-btn" aria-label="Home" title="Home">
<i class="fa-solid fa-house"></i>
</a>
{% endunless %}
<button
class="theme-toggle"
onclick="(function(){
var next = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
document.documentElement.classList.add('theme-transitions-enabled');
})()"
aria-label="Toggle color theme"
title="Toggle color theme"
>
<i class="fa-solid fa-moon icon-moon"></i>
<i class="fa-solid fa-sun icon-sun"></i>
</button>
<img src="{{ '/assets/pfp.gif' | relative_url }}" alt="Bryan Ramos" class="pfp">
<h1>Bryan Ramos</h1>
<p class="header-tagline">Developer &amp; Technologist</p>
<ul>
<li><a href="mailto:bryan@ramos.codes" aria-label="Email"><i class="fa-solid fa-envelope"></i></a></li>
<li><a href="https://github.com/itme-brain" aria-label="GitHub"><i class="fa-brands fa-github"></i></a></li>
<li><a href="{{ '/pgpkey' | relative_url }}" aria-label="PGP Key"><i class="fa-sharp fa-solid fa-key"></i></a></li>
<li><a href="https://www.linkedin.com/in/bryan-ramos-ab467228a/" aria-label="LinkedIn"><i class="fa-brands fa-linkedin-in"></i></a></li>
<li><a href="{{ '/blog' | relative_url }}" aria-label="Blog"><i class="fa-solid fa-blog"></i></a></li>
</ul>
</header>

11
_layouts/base.html Normal file
View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
{% include head.html %}
<title>{% if page.title %}{{ page.title }} | {% endif %}Bryan Ramos</title>
</head>
<body>
{% include header.html %}
{{ content }}
</body>
</html>

18
_layouts/blog.html Normal file
View file

@ -0,0 +1,18 @@
---
layout: base
---
<div class="blog-list">
{% if site.posts.size == 0 %}
<p style="color: var(--color-text-secondary);">No posts yet.</p>
{% else %}
{% for post in site.posts %}
<div class="post-preview">
<span class="post-date">{{ post.date | date: "%B %-d, %Y" }}</span>
<a href="{{ post.url | relative_url }}">{{ post.title }}</a>
{% if post.description %}
<p class="post-description">{{ post.description }}</p>
{% endif %}
</div>
{% endfor %}
{% endif %}
</div>

4
_layouts/page.html Normal file
View file

@ -0,0 +1,4 @@
---
layout: base
---
{{ content }}

15
_layouts/post.html Normal file
View file

@ -0,0 +1,15 @@
---
layout: base
---
<article class="post">
<header class="post-header">
<h1>{{ page.title }}</h1>
<time class="post-date" datetime="{{ page.date | date: '%Y-%m-%d' }}">{{ page.date | date: "%B %-d, %Y" }}</time>
</header>
<div class="post-content">
{{ content }}
</div>
<footer class="post-nav">
<a href="{{ '/blog' | relative_url }}">← Back to Blog</a>
</footer>
</article>

View file

@ -0,0 +1,58 @@
---
layout: post
title: "Setting Up PGP: Why Encrypted Communication Still Matters"
date: 2023-02-28
description: "A practical look at PGP, why I finally got around to setting up my own key, and why you probably should too."
tags: [security, pgp, privacy]
---
I've been putting off setting up a PGP key for years. It always felt like one of those things that lived in the intersection of "I should do this" and "how many people actually email me sensitive information." The answer to the second question is not many, but that's kind of beside the point.
This week I finally did it, published my public key on this site, and wanted to write down why I think it still matters in 2023.
## What PGP Actually Does
PGP (Pretty Good Privacy) is an encryption standard that lets two people communicate privately, even over completely untrusted channels like email. The core idea is simple: you generate a key pair. One key is public and you give it to anyone who might want to send you something private. The other key is private and never leaves your machine.
When someone wants to send you an encrypted message, they use your public key to lock it. Only your private key can open it. No server in the middle, no account needed, no company storing your messages.
It also works in reverse for signatures. You can sign something with your private key and anyone with your public key can verify the signature came from you. This is useful for verifying software downloads, signing git commits, or just proving a message wasn't tampered with.
<!-- Replace with a terminal screenshot showing gpg --gen-key output or your key fingerprint -->
![GPG key generation in a terminal](/assets/img/pgp-keygen.png)
## Why It Feels Abandoned (And Why That's Wrong)
The honest reason most people haven't set up PGP is that the tooling is rough and the UX has barely changed since the 90s. Keyservers are a mess, key discovery is unreliable, and the average person isn't going to paste an ASCII armor block into a web form to send their friend a message.
Signal exists. Matrix exists. These are genuinely better tools for real-time encrypted chat with non-technical people.
But email isn't going away, and there are specific situations where PGP is still the right tool. Submitting a security vulnerability report. Communicating with people who run their own mail servers. Signing software releases. Verifying the identity of someone you're meeting for the first time online.
The other thing PGP gives you that Signal doesn't is longevity and portability. Your key lives on your hardware. No company can deplatform you, no app can be removed from an app store, no terms of service can change. It's just math.
## Getting Started
The quickest path on Linux is through GnuPG:
```bash
gpg --full-generate-key
```
Choose RSA 4096 or Ed25519. Give it your email address. Set a passphrase. That's your key.
To export your public key so you can share it:
```bash
gpg --armor --export your@email.com
```
Paste that block on your website, upload it to keys.openpgp.org, or just email it to people. Once someone has your public key, they can encrypt messages that only you can read.
I've posted mine on the [PGP key](/pgpkey) page here. If you're sending me anything sensitive, please use it.
## One More Thing
Beyond privacy, generating your own key is a small but meaningful act of taking responsibility for your own security infrastructure. You're not trusting a company to hold your keys. You're not hoping an app stays funded and maintained. The cryptography has been audited for decades and it works.
That's worth the afternoon it takes to set up.

View file

@ -0,0 +1,44 @@
---
layout: post
title: "Why I'm Paying Attention to Nostr"
date: 2023-04-08
description: "Nostr is rough around the edges and the user base is tiny. I'm watching it anyway."
tags: [nostr, decentralization, social-media, bitcoin]
---
I set up my Nostr identity a few weeks ago. It took longer than it should have, involved too many copy-pasted hex strings, and the client I ended up settling on still crashes occasionally. I'm writing about it anyway because I think the underlying idea is interesting enough to track.
## What Nostr Is
Nostr stands for "Notes and Other Stuff Transmitted by Relays." The name is intentionally generic because the protocol is intentionally minimal. At its core it's just a specification for cryptographically signed JSON messages that get broadcast to relay servers. Any client can connect to any relay. Any relay can store any message. The only identity system is a public/private key pair.
There's no company, no central server, no account to create. Your identity is your key. Your posts are signed by that key. Relays can choose what to store and what to drop, but they can't forge your signature or impersonate you.
<!-- Replace with a screenshot of your Nostr profile or a client like Damus/Iris showing your npub -->
![Nostr client showing a profile feed](/assets/img/nostr-client.png)
## Why This Feels Different
I've been on Twitter since around 2010. I've watched it go through multiple cycles of policy changes, API lockdowns, and relationship rewrites between the company and its users. The pattern is always the same: the platform grows, the platform becomes valuable, the platform starts extracting value from users and developers who helped make it valuable.
Mastodon tried to solve this with federation. The problem is that federation still means trusting whoever runs your instance. You're one admin burnout or one bad actor away from your data disappearing or your account being frozen. The ActivityPub model also has a moderation coordination problem where instances end up playing a constant defederation game.
Nostr's approach is different. Because your identity is a key you control, no relay operator can take your identity away. You can switch relays, run your own relay, or use multiple relays simultaneously. The protocol is simple enough that there are now dozens of clients across every platform, and they all interoperate because they're all just reading and writing the same JSON format.
## The Honest Caveats
It's early and it shows. Key management is a real problem. If you lose your private key, you lose your identity and there's no recovery. Most clients have no good answer for this. The onboarding experience for non-technical users is rough.
The user base right now skews heavily toward Bitcoin and cypherpunk communities. That's fine as a starting point but it limits the conversational surface area considerably. If you're not interested in those topics, your feed is going to be thin.
Spam and discovery are unsolved problems. Without central moderation, finding good content requires knowing who to follow, and knowing who to follow requires already being embedded in communities that point you toward people worth following.
## Why I'm Still Here
Despite all that, I set up NIP-05 verification on this domain (which is why you can find me as `bryan@ramos.codes` on Nostr clients that support it) and I'm going to keep posting. The bet I'm making is that the protocol has the right properties to survive even if individual clients and relays come and go.
The thing that keeps bringing me back is the simplicity. I can read the spec in an afternoon. I can run a relay on a small VPS. I can write a client in a weekend if I wanted to. That kind of simplicity is usually a durable property in protocols.
Whether it reaches critical mass or stays a small technical community, I don't know. But the design is right in ways that matter.
Find me on Nostr: `npub17374whevgs040xkd48gr99g0xmpxd9snqt57dsfvtp0jcjt8yjeq49rdyt`

View file

@ -0,0 +1,42 @@
---
layout: post
title: "Twitter Becomes X: The Case for Open Protocols"
date: 2023-08-12
description: "Yesterday Twitter became X. It doesn't really matter, and that's the whole problem."
tags: [social-media, nostr, decentralization, twitter]
---
Yesterday Twitter became X. The bird is gone, the brand is gone, and a social network that defined a decade of internet culture is now named after a PayPal spinoff from 2000. The discourse around this has been predictably loud, but I want to talk about something that gets less attention: why it doesn't actually matter what they rename it, and why that's the most damning thing I can say about the whole situation.
## You Don't Own Your Presence There
When Twitter dies, or transforms into something unrecognizable, everyone who built an audience there discovers the same thing: they were building on rented land. The followers, the links pointing at their profile, the years of posts — none of it is theirs in any meaningful sense. The platform owns the relationships. The platform owns the distribution.
This is not a new observation. It's been said every time a major platform has pivoted, died, or changed its terms. MySpace, Tumblr, Vine, Google+. We watched it happen and we kept building on closed platforms anyway because that's where the people were.
<!-- Replace with a screenshot of the old Twitter bird logo next to the X logo, or a terminal showing a curl to api.twitter.com returning 403 after API lockdowns -->
![The Twitter to X transition](/assets/img/twitter-x-rebrand.png)
## What Actually Changed This Year
The rebrand is cosmetic. What's more significant is the systematic destruction of the API ecosystem. Third-party clients are gone. The API costs that killed them were not a mistake or a miscalculation. They were a policy decision to force everyone through official clients that can be monetized and monitored.
The rate limits on reading. The removal of the free tier. The requirement to pay for basic functionality that was free for a decade. Each of these individually could be explained away. Together they paint a clear picture of a platform that has decided its relationship with developers and power users is adversarial.
The people building on this platform subsidized its growth. They got API access and a network in return. That deal has been unilaterally cancelled.
## The Protocol Alternative
I've been on Nostr for a few months now. I wrote about it in April. It's still rough and the user base is still small, but this week it became significantly more relevant to me.
The difference is fundamental. On Nostr, I hold my own private key. My identity can't be revoked by a board decision or a new CEO. My posts are signed by me and can be verified by anyone without trusting a central server. If a relay goes down, I switch to another relay and my identity goes with me, intact.
This is what a protocol-based approach actually buys you. You're not at the mercy of a company's product roadmap. The clients and relays are interchangeable components. The thing that actually matters, the cryptographic identity and the content, lives with you.
## The Practical Reality
I'm not naive about where the people are. Most of the interesting conversations I've had online over the last decade happened on Twitter, and most of those people are not moving to Nostr anytime soon. Network effects are real and powerful and they don't care about your principles.
But I'm done optimizing my digital presence for platforms I don't control. X can do whatever it wants with its rebrand. I'll keep using it until I don't, and I won't build anything meaningful there that I'd be upset to lose.
The interesting question is what comes after centralized social media. I don't know if Nostr is the answer. But a signed JSON message sent to a relay you can run yourself is at least asking the right questions.

View file

@ -0,0 +1,68 @@
---
layout: post
title: "Real-Time Linux"
date: 2024-06-15
description: "Most Linux systems are good enough. Some systems have strict timing deadlines."
tags: [linux, real-time, systems, kernel]
---
When most people say a system is "fast," they mean it has high throughput. It processes a lot of data per second, or pages load quickly, or builds finish in under a minute. That's a useful property and it's what most software optimization work is aimed at.
Real-time systems are optimizing for something different. They don't care as much about average performance. They care about the worst case. The guarantee they need is not "this will usually respond in 10ms" but "this will always respond in under 1ms, every single time, no exceptions."
That's a fundamentally harder problem, and it's one I work with regularly.
## The Problem with Standard Linux
Linux is a general-purpose operating system. Its scheduler is designed to give all processes a fair share of CPU time, to maximize throughput, and to stay responsive under heavy load. These are good goals for a desktop or a server.
The problem is that achieving fairness and throughput sometimes requires the kernel to hold up other work. Interrupt handlers, memory management, locking operations — the kernel has many internal paths that, in a standard build, cannot be interrupted. If your real-time process needs to run exactly now, but the kernel is in the middle of a non-preemptible section doing something else, your process waits. That wait is called scheduling latency, and in standard Linux it can spike unpredictably.
For a web server, a latency spike of 5ms is noise. For a control system running a simulation where a missed deadline means corrupted state, it's a failure.
<!-- Replace with a screenshot of NightTune showing CPU latency graphs or cyclictest output -->
![Latency measurement output from a real-time Linux system](/assets/img/rt-latency-cyclictest.png)
## What PREEMPT_RT Changes
The PREEMPT_RT patch set, which has been in development since 2005 and was finally merged into the mainline kernel with v6.12 in late 2024, addresses this by making the kernel itself fully preemptible. The key changes:
**Threaded interrupts.** Instead of running interrupt handlers in a context that can't be preempted, PREEMPT_RT converts them to kernel threads that the scheduler can manage like any other thread. A high-priority real-time task can preempt an interrupt handler if needed.
**Priority-inherited mutexes.** Standard Linux spinlocks can create priority inversion: a high-priority task waiting for a lock held by a low-priority task gets stuck behind everything the low-priority task gets preempted by. PREEMPT_RT replaces these with proper mutexes that raise the priority of the lock holder while it's blocking a higher-priority thread.
**Fully preemptible kernel paths.** The long non-preemptible sections in the kernel get eliminated or minimized, bounding the worst-case latency the scheduler can impose.
The result is a kernel where scheduling latency can be measured in microseconds rather than milliseconds, consistently, even under load.
## Tuning Beyond the Kernel
The kernel is only part of the picture. Getting deterministic latency in production requires additional work:
**CPU isolation.** You dedicate specific cores to real-time tasks using `isolcpus` boot parameters, removing them from the general scheduler pool. Background kernel threads, IRQ balancing, and RCU callbacks all get steered away from those cores. The real-time task gets CPU time without competition.
**Interrupt affinity.** Device interrupts get pinned to specific non-isolated cores so they don't interrupt your critical processes. You modify `/proc/irq/N/smp_affinity` directly, and usually disable `irqbalance` entirely since it fights against your configuration.
**Memory locking.** Real-time processes call `mlockall()` at startup to prevent their memory from being paged out. A page fault at the wrong moment will blow your latency budget immediately.
**NUMA awareness.** On multi-socket systems, memory access times depend on which socket the memory is physically on relative to where the CPU is. Binding your real-time process to CPUs and memory on the same NUMA node eliminates a whole class of latency variance.
## Hard vs. Soft Real-Time
Not everything that gets called "real-time" has the same requirements.
**Soft real-time** means deadlines are important and you try hard to meet them, but occasional misses are tolerable. Video playback is soft real-time. A dropped frame is annoying; it doesn't corrupt anything.
**Hard real-time** means missing a deadline is a system failure. Industrial control systems, flight simulation, certain medical devices. The system must guarantee deadline adherence, not just optimize for it statistically.
Most of what real-time Linux enables sits in the hard category. The industries that use it, aerospace, defense, industrial automation, simulation, aren't interested in "usually meets deadlines." They need provable bounds.
## Why This Is Getting More Relevant
The PREEMPT_RT merge into mainline is a significant milestone. For years, using real-time Linux meant carrying an out-of-tree patch set and rebuilding the kernel yourself, which made it harder to maintain and harder to justify in organizations with conservative change management policies.
With real-time capability in the upstream kernel, distributions can ship it as a supported configuration. Red Hat already has RHEL for Real Time. The barrier to adopting real-time Linux in environments that need it keeps dropping.
The systems that need deterministic behavior aren't going away either. Autonomous vehicles, robotics, increasingly complex simulation environments. If anything, the demand is growing.
Understanding what real-time actually means, and what it costs to achieve, is increasingly useful knowledge.

View file

@ -0,0 +1,54 @@
---
layout: post
title: "Why Nostr Never Took Off"
date: 2025-11-16
description: "I was an early Nostr adopter. Two and a half years later, I find myself removing the link from my website after inactivity."
tags: [nostr, social-media, decentralization]
---
I wrote about Nostr in 2023, two and a half years ago. I was cautiously optimistic. The protocol was technically interesting, the key-based identity model was the right idea, and the community was small but engaged. I set up NIP-05 verification on my domain and started posting.
Today I removed the Nostr link from this website. Not because the protocol is gone, it isn't, but because I no longer think it's heading somewhere worth pointing people toward. I want to explain why.
## What It Became
The Nostr feed I checked daily in 2023 was predominantly Bitcoin content. That was expected given the origin story — Nostr grew out of the Bitcoin community, Jack Dorsey funded early development, and the initial network effect came from people already in that space.
What I didn't anticipate was how thoroughly that initial demographic would come to define everything about the platform. By 2024 the dominant conversation wasn't about decentralized protocols or censorship resistance in any general sense. It was a very specific cultural and political monoculture. Post something that didn't fit the consensus worldview and you'd find yourself deprioritized on every major client's algorithm, not through central moderation, but through the informal social mechanics of who runs the popular relays and which follows get amplified.
The "no censorship" promise turned out to mean something narrower: no censorship of the specific things the dominant community wanted to say. Everything else found itself slowly squeezed out not by policy but by indifference and social pressure.
<!-- Replace with a screenshot of a Nostr client feed showing the repetitive content/echo chamber, or your own unfollowed npub profile page -->
![A Nostr client feed in late 2025](/assets/img/nostr-feed-2025.png)
## The Usenet Problem
I've been thinking about how closely this maps to Usenet's decline. Usenet was technically elegant, decentralized before decentralization was a word people used, and destroyed by two things: spam and the collapse of signal-to-noise ratio. Not in that order.
The spam came first and was partially managed through killfiles and moderated groups. But the social problem was harder. Once a community scales past a certain point, the loudest and most persistent voices dominate regardless of quality. The thoughtful people leave because the ratio of effort to reward degrades. What's left is the people who are most motivated to keep posting, which tends to correlate with having the most extreme or commercially motivated things to say.
Nostr repeated this. The relay model doesn't solve discovery; it pushes it to clients and the social graph. If the social graph is already concentrated around a particular community, discovery just reinforces that concentration. Finding interesting people outside the dominant cluster requires already knowing who they are. The protocol has no answer for this.
## Technical Problems That Never Got Solved
The key management problem is still unsolved in any practical sense for average users. Lose your key, lose your identity. Every backup solution involves either trusting someone else with your key (which recreates the custodial problem you were trying to avoid) or being technically sophisticated enough to manage hardware security appropriately.
Relay sustainability never found a clean model. The paid relay experiment had some uptake but the economics are genuinely difficult. Relays that charged too little couldn't cover costs. Relays that charged enough to be sustainable were too expensive for casual users. Free relays became spam sinks that degraded the experience for everyone.
Content discovery across relays remains primitive. The global feed on most clients is unusable noise. Finding good content still requires word of mouth in communities, which means good content in small or niche communities stays invisible.
## What the Protocol Got Right
I want to be fair. The cryptographic identity model is correct. Owning a keypair as your identity is the right design. The protocol simplicity that let dozens of clients get built is genuinely valuable. The fact that there are still active developers working on NIP extensions shows that the core idea has staying power.
The failure isn't that the protocol is wrong. The failure is that a technically correct protocol isn't sufficient to build a healthy social space. You also need thoughtful community cultivation, economic models for infrastructure, and some answer to the discovery problem that doesn't just replicate existing social hierarchies.
None of those are easy. Centralized platforms haven't solved them either, they've just hidden the problem behind algorithms tuned for engagement. But the Nostr community largely believed that solving the technical censorship problem would also solve the social problems, and that turned out not to be true.
## Where I Actually Am Now
I still believe in the principle. Cryptographic identity, open protocols, no single point of control. These are the right building blocks for communication infrastructure that can survive corporate capture.
But building on a protocol that has become an echo chamber isn't advancing those goals, it's just participating in a different kind of monoculture. I'll keep watching what happens with the underlying protocol work. If the community composition changes or the discovery problems get solved in a real way, I'll look again.
For now: the link is gone, the account still exists, and I'm still reachable at `bryan@ramos.codes`.

View file

@ -0,0 +1,11 @@
---
layout: post
title: "Hello New World"
date: 2026-03-03
description: "First post on the new self-hosted blog."
tags: [meta]
---
Migrated away from Substack to keep everything self-hosted and under my own control.
Posts will continue covering whatever I'm currently working on or thinking about.

5
blog.html Normal file
View file

@ -0,0 +1,5 @@
---
layout: blog
title: Blog
permalink: /blog
---

View file

@ -1,89 +1,573 @@
/* =============================================
DESIGN TOKENS DARK (default / :root fallback)
============================================= */
[data-theme="dark"],
:root {
--color-bg: #0F1923;
--color-surface: #162230;
--color-surface-alt: #1C2C3A;
--color-border: #243447;
--color-text-primary: #E8EDF2;
--color-text-secondary: #8FA3B1;
--color-text-muted: #566B7A;
--color-accent: #4EAECF;
--color-accent-hover: #3B9AB8;
--color-quote-bar: #4A9B2A;
--color-link: #E8EDF2;
--color-link-hover: #4EAECF;
--color-toggle-bg: #243447;
--color-toggle-fg: #8FA3B1;
--color-toggle-hover-bg: #2E4259;
--color-toggle-hover-fg: #E8EDF2;
}
/* =============================================
DESIGN TOKENS LIGHT
============================================= */
[data-theme="light"] {
--color-bg: #F5F7FA;
--color-surface: #FFFFFF;
--color-surface-alt: #EEF1F5;
--color-border: #D0D9E2;
--color-text-primary: #1A2733;
--color-text-secondary: #4A6070;
--color-text-muted: #8FA3B1;
--color-accent: #1A7FA0;
--color-accent-hover: #155E78;
--color-quote-bar: #3A8520;
--color-link: #1A2733;
--color-link-hover: #1A7FA0;
--color-toggle-bg: #E0E8F0;
--color-toggle-fg: #4A6070;
--color-toggle-hover-bg: #C8D8E8;
--color-toggle-hover-fg: #1A2733;
}
/* =============================================
LAYOUT & TYPOGRAPHY VARIABLES
============================================= */
:root {
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 1.875rem;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
--leading-tight: 1.25;
--leading-normal: 1.6;
--leading-relaxed: 1.75;
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-5: 1.25rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-10: 2.5rem;
--space-12: 3rem;
--max-width: 740px;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;
--transition-fast: 150ms ease;
--transition-base: 200ms ease;
}
/* =============================================
RESET + BASE
============================================= */
*, *::before, *::after {
box-sizing: border-box;
}
html { html {
display: flex; display: flex;
justify-content: center; justify-content: center;
margin: auto; margin: auto;
max-width: 100%; max-width: 100%;
color: white; background-color: var(--color-bg);
background-color: #1B262C; color: var(--color-text-primary);
padding: 0; padding: 0;
border: 0; border: 0;
} }
body { body {
padding: 10px 10px; padding: var(--space-4) var(--space-4);
margin: 20px auto; margin: var(--space-8) auto;
font-size: 1.0rem; font-size: var(--text-base);
max-width: 800px; font-family: var(--font-sans);
font-family: sans-serif; font-weight: var(--weight-normal);
line-height: var(--leading-normal);
max-width: var(--max-width);
width: 100%;
}
/* =============================================
HEADINGS
============================================= */
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-sans);
font-weight: var(--weight-semibold);
line-height: var(--leading-tight);
color: var(--color-text-primary);
} }
h1 { h1 {
font-size: var(--text-2xl);
margin: 0; margin: 0;
border: 0;
padding: 0; padding: 0;
border: 0;
text-align: center; text-align: center;
letter-spacing: -0.015em;
} }
h2 { h2 {
font-size: 95%; font-size: var(--text-xl);
border: 0; border: 0;
padding: 0; padding: 0;
margin-top: var(--space-8);
margin-bottom: var(--space-4);
} }
ul { h3 {
display: flex; font-size: var(--text-lg);
flex-direction: row; margin-top: var(--space-6);
justify-content: center; margin-bottom: var(--space-3);
padding-right: 3rem;
}
li {
display: flex;
justify-content: center;
font-size: 25px;
border: 0;
margin: 0;
padding: 0.5rem;
}
#custom-substack-embed {
display: flex;
justify-content: center;
}
.shill {
display: flex;
justify-content: center;
} }
/* =============================================
LINKS
============================================= */
a, a:active { a, a:active {
color: inherit; color: var(--color-link);
text-decoration: none; text-decoration: none;
transition: color var(--transition-fast);
} }
a:hover { a:hover {
color: gray; color: var(--color-link-hover);
}
/* =============================================
HEADER
============================================= */
header {
text-align: center;
padding-bottom: var(--space-8);
border-bottom: 1px solid var(--color-border);
margin-bottom: var(--space-8);
position: relative;
} }
.pfp { .pfp {
display: block; display: block;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
border-radius: 50%; border-radius: var(--radius-full);
width: 180px; width: 120px;
height: 180px; height: 120px;
margin-bottom: var(--space-4);
border: 2px solid var(--color-border);
} }
.header-tagline {
font-size: var(--text-sm);
color: var(--color-text-secondary);
font-weight: var(--weight-medium);
letter-spacing: 0.08em;
text-transform: uppercase;
margin: var(--space-2) 0 var(--space-6);
}
header ul {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: var(--space-1);
list-style: none;
padding: 0;
margin: 0;
}
header li {
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
border: 0;
margin: 0;
padding: 0;
}
header li a {
display: flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
border-radius: var(--radius-md);
color: var(--color-text-secondary);
transition: color var(--transition-fast), background-color var(--transition-fast);
}
header li a:hover {
color: var(--color-accent);
background-color: var(--color-surface);
}
/* =============================================
HOME BUTTON
============================================= */
.home-btn {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
background-color: var(--color-toggle-bg);
color: var(--color-toggle-fg);
font-size: 14px;
transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.home-btn:hover {
background-color: var(--color-toggle-hover-bg);
color: var(--color-toggle-hover-fg);
border-color: var(--color-accent);
}
/* =============================================
THEME TOGGLE
============================================= */
.theme-toggle {
position: absolute;
top: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
background-color: var(--color-toggle-bg);
color: var(--color-toggle-fg);
cursor: pointer;
font-size: 14px;
-webkit-appearance: none;
appearance: none;
line-height: 1;
transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.theme-toggle:hover {
background-color: var(--color-toggle-hover-bg);
color: var(--color-toggle-hover-fg);
border-color: var(--color-accent);
}
.theme-toggle .icon-moon { display: inline-block; }
.theme-toggle .icon-sun { display: none; }
[data-theme="light"] .theme-toggle .icon-moon { display: none; }
[data-theme="light"] .theme-toggle .icon-sun { display: inline-block; }
/* =============================================
THEME TRANSITION (only fires on user click)
============================================= */
.theme-transitions-enabled,
.theme-transitions-enabled * {
transition-property: color, background-color, border-color;
transition-duration: var(--transition-base);
transition-timing-function: ease;
}
/* =============================================
HOMEPAGE SECTIONS
============================================= */
.section-bio {
margin-bottom: var(--space-10);
}
.section-bio p {
line-height: var(--leading-relaxed);
color: var(--color-text-primary);
margin-bottom: var(--space-4);
}
.section-heading {
font-size: var(--text-xs);
font-weight: var(--weight-semibold);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--color-text-muted);
margin-top: 0;
margin-bottom: var(--space-4);
padding-bottom: var(--space-2);
border-bottom: 1px solid var(--color-border);
}
.section-skills {
margin-bottom: var(--space-10);
}
.skills-grid {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
}
.skill-tag {
display: inline-flex;
align-items: center;
padding: var(--space-1) var(--space-3);
font-size: var(--text-sm);
font-weight: var(--weight-medium);
color: var(--color-text-secondary);
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-full);
line-height: 1.5;
transition: color var(--transition-fast), border-color var(--transition-fast), background-color var(--transition-fast);
}
.skill-tag:hover {
color: var(--color-accent);
border-color: var(--color-accent);
background-color: var(--color-surface-alt);
}
.section-recent-posts {
margin-bottom: var(--space-10);
}
.view-all-posts {
margin-top: var(--space-4);
font-size: var(--text-sm);
}
.view-all-posts a {
color: var(--color-accent);
}
.view-all-posts a:hover {
color: var(--color-accent-hover);
}
/* =============================================
BLOG INDEX
============================================= */
.blog-list {
margin-top: var(--space-4);
}
.post-preview {
padding: var(--space-5) 0;
border-bottom: 1px solid var(--color-border);
}
.post-date {
font-size: var(--text-xs);
font-weight: var(--weight-medium);
letter-spacing: 0.06em;
color: var(--color-text-muted);
text-transform: uppercase;
display: block;
margin-bottom: var(--space-1);
}
.post-preview a {
font-size: var(--text-base);
font-weight: var(--weight-medium);
color: var(--color-text-primary);
display: block;
margin-bottom: var(--space-1);
}
.post-preview a:hover {
color: var(--color-accent);
}
.post-description {
font-size: var(--text-sm);
color: var(--color-text-secondary);
margin-top: 0;
line-height: var(--leading-normal);
}
/* =============================================
BLOG POST
============================================= */
.post-header {
margin-bottom: var(--space-8);
padding-bottom: var(--space-6);
border-bottom: 1px solid var(--color-border);
}
.post-header h1 {
text-align: left;
font-size: var(--text-2xl);
letter-spacing: -0.02em;
margin-bottom: var(--space-2);
}
.post-content {
line-height: var(--leading-relaxed);
}
.post-content img {
max-width: 100%;
height: auto;
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
margin: var(--space-6) 0;
display: block;
}
.post-content h2,
.post-content h3 {
margin-top: var(--space-8);
margin-bottom: var(--space-3);
}
.post-content p {
margin-bottom: var(--space-5);
}
.post-content code {
background-color: var(--color-surface-alt);
color: var(--color-accent);
padding: 0.1em 0.35em;
border-radius: var(--radius-sm);
font-family: var(--font-mono);
font-size: 0.875em;
}
.post-content pre {
background-color: var(--color-surface-alt);
border: 1px solid var(--color-border);
padding: var(--space-5);
border-radius: var(--radius-md);
overflow-x: auto;
margin-bottom: var(--space-6);
}
.post-content pre code {
background: none;
color: var(--color-text-primary);
padding: 0;
font-size: var(--text-sm);
}
.post-content blockquote {
border-left: 3px solid var(--color-quote-bar);
margin-left: 0;
margin-right: 0;
padding: var(--space-3) var(--space-5);
background-color: var(--color-surface);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
color: var(--color-text-secondary);
margin-bottom: var(--space-5);
}
.post-nav {
margin-top: var(--space-10);
padding-top: var(--space-5);
border-top: 1px solid var(--color-border);
font-size: var(--text-sm);
}
.post-nav a {
color: var(--color-text-secondary);
}
.post-nav a:hover {
color: var(--color-accent);
}
/* =============================================
PGP KEY PAGE
============================================= */
.gpg { .gpg {
display: inline-block; display: block;
background-color: #222831; background-color: var(--color-surface-alt);
border-radius: 15px; border: 1px solid var(--color-border);
border-radius: var(--radius-md);
max-width: 100%; max-width: 100%;
word-wrap: break-word; word-wrap: break-word;
word-break: break-all;
padding: var(--space-5);
font-family: var(--font-mono);
font-size: var(--text-xs);
line-height: var(--leading-relaxed);
color: var(--color-text-secondary);
overflow-x: auto;
margin-top: var(--space-4);
} }
.download { .download {
webkit-user-select: none; display: inline-flex;
ms-user-select: none; align-items: center;
user-select: none; gap: var(--space-2);
padding: var(--space-2) var(--space-4);
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
font-size: var(--text-sm);
font-weight: var(--weight-medium);
color: var(--color-text-secondary);
transition: color var(--transition-fast), border-color var(--transition-fast);
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.download:hover {
color: var(--color-accent);
border-color: var(--color-accent);
}
/* =============================================
RESPONSIVE
============================================= */
@media (max-width: 600px) {
body {
padding: var(--space-4) var(--space-3);
margin-top: var(--space-5);
}
h1 {
font-size: var(--text-xl);
}
.pfp {
width: 96px;
height: 96px;
}
.theme-toggle {
width: 32px;
height: 32px;
font-size: 12px;
}
} }

View file

@ -1,76 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/styles.css">
<script src="https://kit.fontawesome.com/f26d369dc4.js" crossorigin="anonymous"></script>
<script src="https://substackapi.com/widget.js" async></script>
<title>Developer / Technologist</title>
</head>
<header>
<img src="assets/pfp.gif" alt="avatar" class="pfp">
<h1>Bryan Ramos</h1>
<ul>
<li><a href="mailto:bryan@ramos.codes" class="fa-solid fa-envelope"></a></li>
<li><a href="https://github.com/itme-brain" class="fa-brands fa-github"></a></li>
<li><a href="pgpkey.html" class="fa-sharp fa-solid fa-key"></a></li>
<li><a href="https://iris.to/#/profile/npub17374whevgs040xkd48gr99g0xmpxd9snqt57dsfvtp0jcjt8yjeq49rdyt"
class="fa-sharp fa-solid fa-feather"></a></li>
<li><a href="https://itmebrain.substack.com" class="fa-solid fa-blog"></a></li>
</ul>
</header>
<body>
<h2>
Check out my work on Github.</br></br>
Contact with inquiries using email or visit my socials using the above links.</br></br>
Please encrypt any sensitive information using the provided PGP key.</br></br>
</h2>
<div class="causes">
<h1>Projects Supported</h1></br>
<a href="https://www.linuxfoundation.org/about/donate">Linux Foundation</a></br>
<a href="https://www.fsf.org/about/ways-to-donate/">Free Software Foundation</a></br>
<a href="https://bitcoin.org/en/bitcoin-core/contribute/">Bitcoin Core</a></br>
<a href="https://zeusln.app/about">Zeus App</a></br>
<a href="https://github.com/ElementsProject/lightning">Core Lightning</a><br>
<a href="https://github.com/nostr-protocol/nostr">Nostr</a></br>
<a href="https://nixos.org/donate.html">NixOS Foundation</a></br>
<a href="https://donate.torproject.org/">Tor Project</a></br>
</div></br></br>
<footer>
<p class="shill">
Stay in touch and keep up-to-date with any articles I may publish
</p>
<div id="custom-substack-embed">
<script>
window.CustomSubstackWidget = {
substackUrl: "itmebrain.substack.com",
placeholder: "example@gmail.com",
buttonText: "Confirm",
theme: "custom",
colors: {
primary: "#54982D",
input: "#1B262C",
email: "#A69F9F",
text: "#FFFFFF",
}
};
</script>
</div>
</footer>
</body>
</html>

48
index.md Normal file
View file

@ -0,0 +1,48 @@
---
layout: page
title: Developer / Technologist
---
<section class="section-bio" markdown="1">
I work at the intersection of systems programming, real-time computing, and open-source infrastructure. My background spans Linux systems, simulation engineering and low-level development in C and Python with a growing focus on applied AI and LLM integration. I have a strong preference for auditable, modular stacks and tools that empower user autonomy.
Reach me by [email](mailto:bryan@ramos.codes) or find me on the platforms above.
Please encrypt sensitive messages using my [PGP key](/pgpkey).
</section>
<section class="section-skills">
<h2 class="section-heading">Stack &amp; Tools</h2>
<div class="skills-grid">
<span class="skill-tag">C</span>
<span class="skill-tag">Python</span>
<span class="skill-tag">Bash</span>
<span class="skill-tag">Linux</span>
<span class="skill-tag">Real-Time Systems</span>
<span class="skill-tag">Simulation Engineering</span>
<span class="skill-tag">AI / LLM Integration</span>
<span class="skill-tag">Bitcoin &amp; Lightning</span>
<span class="skill-tag">Nostr Protocol</span>
<span class="skill-tag">PGP/GPG</span>
<span class="skill-tag">Tor</span>
<span class="skill-tag">Self-Hosting</span>
</div>
</section>
<section class="section-recent-posts">
<h2 class="section-heading">Recent Posts</h2>
<div class="blog-list recent-posts">
{% for post in site.posts limit:3 %}
<div class="post-preview">
<span class="post-date">{{ post.date | date: "%B %-d, %Y" }}</span>
<a href="{{ post.url | relative_url }}">{{ post.title }}</a>
{% if post.description %}
<p class="post-description">{{ post.description }}</p>
{% endif %}
</div>
{% endfor %}
</div>
<p class="view-all-posts"><a href="{{ '/blog' | relative_url }}">View all posts &rarr;</a></p>
</section>

View file

@ -1,14 +1,8 @@
<!DOCTYPE html> ---
<html lang="en"> layout: page
<head> title: PGP Key
<meta charset="UTF-8"> permalink: /pgpkey
<meta http-equiv="X-UA-Compatible" content="IE=edge"> ---
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/styles.css">
<title>Bryan Ramos - PGP Key</title>
</head>
<body>
<a href="assets/public.key" class="download">🔑Key File <a href="assets/public.key" class="download">🔑Key File
<br/><br/></a> <br/><br/></a>
@ -123,6 +117,4 @@ OEpwdDwa67AtzYKG0ssOJI+po9TlbKYS4O4H8XnPhYSOEw8eObNPYCX7jyAjXloo<br/>
1hbflYLyMYo1BxGR6bPS9gJA2w==<br/> 1hbflYLyMYo1BxGR6bPS9gJA2w==<br/>
=5uun<br/> =5uun<br/>
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
</div> </div>
</body>
</html>