Jim's Depository

this code is not yet written

Femtoblogger gained support for Passkeys and Atom.

You can create an account with passkey authentication. If you have an existing account, you can add another passkey. The username/password authentication is still supported, but not encourage. Notably missing is the ability to set or change a password once you have an account, I'll get to that.

Atom support is back for all you feed readers. Another Codex win. I asked it to put atom support back, told it which endpoint I wanted to use. It looked around at my models and how I do things and wrote it all in one whack. It really is a bit like wishing features into existence.

Less visible, some sorts of pages are now cached internally so they don't have to be regenerated all the time. For instance, the Atom feed page caches so if your reader uses If-None-Match or If-Modified-Since you might get a 304 Not Modified and save a download. But even if you don't use those headers you will probably get a pregenerated cached copy, so that saves time.

I had Codex help me with the parts of femtoblogger which annoyed me.

As I said earlier, I was ambivalent about Lighter. I decided to ditch Lighter and just go straight to llibsqlite3. Codex and I stomped through that table by table in my model layer with me asking it to convert a table and it doing all the work. 100% success and much faster than I would have done it by hand.

The I decided to address the HTML generation. I'd been using Stencil and template files. I asked Codex to make me a HTML DSL so I could write my HTML in Swift and use Swift for the flow of control and computed values instead of trying to map that on to the Stencil capabilities. 100% success and much faster than I would have done by hand. Codex was reading the Stencil templates and generating the HTML DSL with interspersed Swift control flow and values which also extending the DSL every time it found an unimplemented HTML element. I had to do a little steering on the shape of the DSL API to make it come out the way I wanted, so not an unattended change, but easily done 10 times faster than by hand and without all the tiny errors I would have introduced.

I've finished rewriting femtoblogger again. Now it is in Swift 6 concurrency using the Hummingbird 2 web framework.

Thoughts:

  • Swift 6 is surprisingly good at identifying concurrency foot guns. Sometimes it is obtuse about corrective action, but it caught a number of cases where I would have risked uncontrolled concurrent access.
  • Hummingbird is ok, but I find it missing some features which should really be included. There are places to hook them in, and they generally write what you need in the documentation, but it would be nicer if they were included. (e.g.: access to the remote IP in the request context, or parsing multipart/form-data.
  • For building on Debian, the binaries are tied to the Swift Stdlib which might not be synced on your deploy machine, so I've resorted to swift build --static-swift-stdlib in my Makefile and keeping the Swift Stdlib embedded.
  • For simple packages, you can organize all the parts in a directoy at build time and use dpkg-deb --build --root-owner-group to make a Debian package with minimal fuss. If you need to screw with file owners other than root then you'll have to get into a fakeroot or something. The dpkg-deb man page alludes to them adding a owner/mode manifest, so I'd hold out for that.
  • brew can install dpkg on your Mac so you can test building the package there. You'll get your macos binary, so it isn't usable. If you used the Swift Static SDK you could build your Debian packages on the mac, but I didn't want to mess with that.
  • I used Lighter to access the database, but I'm not sure I like it. The API has like three ways of doing everything and doesn't really talk about them, so it's kind of random exploration to get something to work. It makes a set of struct to map to your SQLite tables which is kind of its thing, but I kept finding myself working with partial data so bypassing that layer on insert, update, or delete. For reading I ended up making my own structs anyway for reasons on about half the tables. Still, I like the strongly typed access.

So, maybe I'll write more here now that the software works again.

The Google scanned indicator has been updated to this decade's aesthetic. Also, it and the verified user indicator have been brought into the CSS to prevent loading image resources.

I'm not totally happy about that. They are spans with images embedded, I'd rather have them be images with shared content for the sake of alt tags. But oh well.

Glad to see you tested the colors in light mode this time.

Beginning now, well, slightly ago… all post authors get a blue checkmark by their name.

This feature sucks. It has poor contrast in light mode and hurts my eyes. You should do broader accessibility testing before rolling out major new features. I think the developers must all use dark mode all the time.

If you are thinking things changed, you are probably getting the dark mode support. It's your browser's choice. I send the same stuff to you, but the CSS is now littered with @media (prefers-color-scheme: dark) { sections to change things up for dark mode.

I wish Safari didn't put all the editable text, radio buttons, and buttons in blinding white. I wonder if I gorked something up there.

Update: Yeah. It was me. Somewhere in the process of switching to use CSS variables for my colors the problem went away. I probably had a background forced to something unfortunate.

I've switched from the Ink markdown processor to Down. I liked that Ink was pure swift, but I kept spending time enhancing it to meet my needs. Down uses libcmark, which I'm not wild about having inside my server, but hopefully I won't spend any more time thinking about it.

It's a testament to both that swapping took just a couple minutes. Half of it finding the typo in my Packages.swift file.

print("Hello World! I'm beautiful!")

As long as I was fiddling with femtoblogger I touched up the CSS so code will stand out a little better, and then splurged and added Prism to highlight source code for you. I feel a little bad about the 12k of javascript, but you only load it once. We will survive this.

So far I recommend Prism. It was a CSS add in <HEAD> and a <SCRIPT> add by </BODY> and everything worked.

The biggest add to femtoblogger is an out of band management interface, but you don't get to see that.

I was typing up my thoughts on using SwiftUI and broke femtoblogger! My screed gets truncated about half way through.

I'm not sure if that means I should:

  • settle down?
  • fix femtoblogger?
  • tighten up my writing?

Who am I kidding… where did I leave that source code…

Update: I try to be a good modern programmer and haul in functionality from strangers on GitHub instead of spending weeks writing it myself… and then… BAM. The Markdown to HTML translator I'm using would allow raw HTML through, which in a public environment is irresponsible, so I kill any HTML I find. Sadly, if you include <table> in your comment, the markdown engine thinks everything from the opening tag to the end is the HTML and I kill it.

I guess I'll have to add a real "safe mode" to the markdown translator. The author hasn't integrated a pull request in months, that is kind of deterrent to making nice additions.

Update: Three hours to study the markdown processor enough to figure out how to cleanly add a safe mode and tests. I'd have been done faster until I realized it was written by John Sundell. Google frequently brings me his articles for solving Swift conundrums. He deserves an extra tidy patch.

The robots are reading at least. The rate is low enough I'll just manually delete them for now.

I used to use the google recaptcha, but I know I'm annoyed at finding stop lights and street signs and in a more tracking sensitive era I don't feel like feeding people's reading habits to third parties.

That probably means I'll eventually have to make my own human detector system and lose a week to that.

Well that was a fair bit of gap in between posts.

Femtoblogger is now on its 4th implementation. I feel the need to try out Swift on the server, so here we are. 100% pure Swift Femtoblogger.

The previous incarnation was Go. Before that was PHP. There was probably a never deployed Python version in there too.

Thoughts on Swift and the design of the server this time around…

  • I very much like the type safety of Swift. When I code in Swift there are no runtime explosions, and very few bugs discovered in testing.
  • The Swift code is more information dense than the Go code was. The Go code felt like a great sea of text with a little bit of content. The Swift code packs a lot of content into a screen of code.
  • Performance appears to be a non-issue. SQLite backed pages are serving in 3-12ms. For comparison I added a caching layer for most popular queries and dropped that into the 80µs range for the query handling. That's not real, I handle them by doing a X-Accel-Redirect header back to Nginx and letting it serve a file out of a RAM disk directly. But it means my process is not going to be buried.
  • I haven't bothered with any compiler optimization. I want the checks to stay in place while I see if I've screwed up somewhere.

The Network End of Things

  • The Swift server network library, NIO, really wants to push you into a futures and promises style of programming. I resisted because it is so clumsy at the source code level. Your code begins to vanish under a sea of ritual. That said, I probably would have been better off to embrace it. A request needs to get out of the network queues to free them up while it gets in a non-concurrent model queue to work with the data, then out of there as soon as possible and into a render queue to do template expansion and back into the network queues to send the data out. It's a lot to coordinate.
  • I suppose I'll continue avoiding futures and promises in femtoblogger until Swift 6 and the beginning of its new concurrency model make it into NIO.
  • I didn't use Vapor or similar web frameworks, I build my server directly on NIOHTTP1 so I'd understand the architecture. The first 90% was simple! Then I needed to parse some form data and couldn't find the calls in the API. Eventually I found an email in a filing cabinet behind a sign that said "Beware of the Leopard" where the NIO team proclaimed that "out of scope". Maybe I'll make a post on parsing "multipart/form-data" (which can't be a String because it might not be valid) and parsing HTTP header values.

Using Other People's Packages

  • I did reach out for 'random dude' packages for a higher level SQLite API, Markdown to HTML conversion, and template expansion. It's a bit of a mixed bag there. I'm glad I did it, but there were issues. As always it seems to take a lot of research time to figure out which solution is well supported, not horrible, and will likely last a while.
  • I used SQLite.swift for accessing SQLite. The tutorial writing crowd on the internet likes it. It gives you a type safe way of interacting with SQLite without too much ritual. The package is a few years old and has been dormant for most of a year. I found a design flaw where it would try to read a mismatched datatype and explode your process. (Just because you called a SQLite column TEXT doesn't mean it isn't going to return a BLOB for one of the rows.) It looked to be an involved fix to use the receiver's datatype instead of the provider's, and in the absence of an active maintainer I didn't want to invest in making the changes. It was easier to rewrite all the rows to have their TEXT really be TEXT.
  • I used Ink for Markdown to HTML conversion. It is simple to use. Out of the box it choked on CRLF line endings. The patch ended up being a , "\r\n" in just the right place, plus about 16 lines of test cases. Hint: "\r\n" is a single Character in Swift… grapheme clusters and all. The maintainer is active so I sent that one in.
  • I used Stencil for template expansion. I've used it before and the rough edges I used to have to hammer off are now addressed. Note: Stencil could really use a SafeExpansion mode which escapes HTML for when you have untrusted users. It has user created filters, I ended up making one instead of forking and adding a safe expansion feature.
more articles