Tag: colophon

Publishing blog posts from Obsidian

Obsidian Blog Publish Plugin

I've been publishing blog posts from an authenticated /admin page on this site using GitHub credentials to authenticate myself. This has always been concerning to me as a security vulnerability. I wrote the C# auth handler myself but I'm no security expert especially when it comes to OAuth. I've had a lot of exposure to it but have never considered myself an expert on it.

The second issue with the admin page is that it has been eating my blog posts lately. I would author a post in the admin page and when I hit submit, the form would submit and the post would just get silently dropped. I tried debugging it for a while but decided the complexity and security of the admin page wasn't worth it anymore.

In the spirit of simplifying the hosting (removing postgres and migrating to fly.io), I decided I could author posts in Obsidian instead and write a plugin to publish directly to the blog's storage blob store (using Azure blob storage). This is the first post being written in Obsidian!

I'm now realizing that I'm back to where I started back in 2023 when I first used Obsidian Publish to host a small blog. I rarely posted on it (even less often than this blog) and shut it down eventually.

One annoying thing with creating the obsidian plugin was that I wanted to use the official Azure JavaScript SDK for the blob storage uploads, but it thinks that Obsidian is a browser and blocks using a shared credential key as the authentication mechanism. It only allows it in NPM runtime environments. I understand why they have to add this check, but it did force me to write my own slim blob store SDK.

With this change, I used AI way less than in the past. Partly because I cancelled my Claude Pro subscription last month and partly because I wanted to learn and understand the Obsidian plugin API and TypeScript development. I originally started this blog to learn and that was happening less and less as I was relying on AI more. I didn't completely abstain however. I used OpenCode and OpenRouter for some parts such as the blob store sdk. I've written my own Blob Store SDKs in C# before and it's just tedious to set up the auth correctly. It was nice to use open router as it allowed me to try out different models, such as DeepSeek V4 and Kimi 2.5. However, there is a reason people flock to Opus and GPT5.5. DeepSeek was very disappointing, it actually felt like GPT 3.5 days in some ways. It would just quite in the middle of work with no notification. I ended up going back to Sonnet 4.6 to finish up some work and the increase in capability was so immediately clear.

I'm glad I got to try out some different tools. At work I use Copilot CLI and can choose between several different models. I tend to use GPT5.5 for planning and Opus 4.7 for executing the implementation specs as it plays to their strengths. I was using Claude Code at work until the licenses were cancelled.

note: I don't use AI to write posts and I don't ever plan to. Writing is sacred to me and helps me think. The point of authoring posts manually is to help me clarify my own thoughts.

How I wrote this post

# / 2026 / 05 / 30

Removing Postgres and storing posts as Markdown files

Now that Heroku is on the deprecation path, I've been looking to move this blog to another provider. One thing I quickly realized is that managed SQL servers are usually not this cheap. I'm currently paying $5 a month for the cheapest tier but I haven't seen another provider with that good of a price point. This got me thinking, why use SQL at all for this simple site? Why not just store posts as markdown with YAML frontmatter?

I've been wanting to try out the acclaimed superpowers plugin for claude code, and this was just the right feature to use it on. Did a long brainstorm session with the agent to flesh out the idea, then using the subagent-driven development method, had Claude implement it. A bunch of subagents got to work and had the whole thing working end-to-end pretty quickly. One thing I forgot to flesh out was the replacement of my custom image-upload JS from my old WYSIWYG editor, trix. We replaced it with EasyMDE and all I had to do was point it at my image upload endpoint and change a bit of formatting on the server side.

I'm very impressed with superpowers. I paired it with Simon Willison's Rodney CLI so the agents could test out their changes and "see" the actual impact of their code in a headless browser. I now have much less code running on the blog and no Postgres dependency, so I should be free to switch to another provider, such as fly.io and hopefully halve my hosting cost.

# / 2026 / 04 / 06

New blog feature: paging

When scrolling to the bottom of the home page, you now should see an "older posts" button which will take you to the next page of posts. This feature has been on my TODO list since day 1 and am happy it's finally implemented. It's much easier now that all posts are cached in memory in the blog, so I can just use LINQ statements to "Skip" and "Take" over the full post collection to emulate paging.

# / 2026 / 01 / 31

Embedding an iframe using Trix

In my previous post about riding to Cle Elum, I ran into issues with Trix removing the iframe I was trying to add to show my cycling route on Ride With GPS. Here is the commit with the fixes.

There were multiple issues that needed to be solved:

  1. Add a custom button that would allow me to put arbitrary HTML into a post (supported feature from Trix)
  2. Modify the DOMPurify config in Trix to allow iframes (also supported feature)
  3. Modify the actual trix.js code to allow iframes (not supported feature, requires forking).

This issue on GitHub helped me get there, but ultimately I needed to find my own path forward that made sense for this site. Claude code also gave me a great start by writing the majority of the trix-extensions.js file, but I had to do a fair amount of manual clean up to get it to the finish line. But it's so nice to have the AI do the boilerplate to begin with, which gives me a great starting point.

The worst part about this fix was having to patch the trix code directly instead of being able to extend it. But since I vendor the trix code in my repo, it was very easy to do. I actually modified the minified trix.js file directly by ctrl+f "iframe" to remove it from the disallow list. If I need to update trix.js in the future, I'll have to remember to re-apply this fix, so this is definitely a tradeoff, but at least it was an easy way to validate the approach.

# / 2025 / 04 / 25

Images!

Spent my free time adding image support to the blog today. I'm using Azure Storage accounts, as it's what I know, and I did not feel like diving into S3 buckets right now, although I probably should at some point.

Once again, .NET Aspire proved its usefulness. I kicked off the changes by adding the Azure Storage hosting and client integrations via Aspire, then added a Minimal API POST endpoint so I had a place to upload the images to. GitHub Copilot is very useful in these kinds of tasks. I can write a comment block describing what I want the endpoint to do, all the edge cases I think it should handle, then hit tab and get 90% of what I wanted. I go through and tweak the rest, then I'm testing almost immediately.

The last piece of the puzzle was hooking up my new endpoint to the Trix editor. Fortunately, the Trix dev team provided a nice Javascript file to show how to hook up the event listeners and use an XHR object to post the image back to the server. Considering my lack of experience with Javascript, I was ecstatic to get this working in only a few iterations.

I edited my last post to add a picture from my bike ride, so I know it's working, but I better add one to this post too :)

Being a goofball with my Instax camera

# / 2025 / 03 / 02

Adding the Trix editor to my Blazor SSR site

Trix: Rich Editor

Reinventing a blog website is so funI've been raw-dogging these posts thus far, manually typing HTML elements into a raw textarea in my admin page so I get something that looks halfway decent.

I decided it was time to upgrade my post editing experience. I was completely unaware of the term WYSIWYG (What You See Is What You Get), but figured that embedding a rich-text editor into a website must be a verysolved problem at this point. So I asked ChatGPT (first 4o, then o3-mini, then o1-pro) for advice on what I should do for this blog. I looked at a few options first, but so many projects are licensed under GPL2 or have weird pricing models. I needed the most bare-bones simple text editor that's totally free to use. That's when I found Trix.

I'm really liking is so far. I was able to integrate it into my Blazor admin page in about 5 minutes, including time figuring out how to make sure the binding of the content gets set up correctly. Here is the commit that shows the integration.

In my testing, it's covered 90% of my use cases for styling in my writing. I think my only grip so far is that you can only insert an h1 heading. Probably not a huge deal, and I may be able to play around with Trix a bit more and figure out how to add a custom toolbar action to insert an h2 heading instead. Or maybe I can just style the h1 in the posts class to be a bit smaller. We'll see.

I have lots of other fun things I want to do with this blog, so it may fall by the way side. I also added an Atom feed to the site today, served at https://davidjarman.net/atom/all.

# / 2025 / 02 / 22