Write Only Log for 2025 - osric.uk

Lego wishlist

  • 30694 Space Science Mech £4
  • 60390 Park Tracktor £8
  • 60362 Car Wash £13
  • 60385 Construction Digger £13
  • 60450 Wheel Loader £13
  • 60461 Tractor with Trailer £18
  • 60439 Space Science Lab £35
  • 7992 Container Stacker £40
  • 60365 Apartment Building £55
  • 60446 Modular Galactic Spaceship £70
  • 60422 Seaside Harbour with Cargo Ship £95
  • 60434 Space Bace and Rocket Launchpad £125

Duplicate entire in edit are/were caused by the owner changing, although thinking about it, Im not sure I have found the cause since the save method was setting the owner, so it should have been set for the first save?

Dreampt I had a lie in, and now I feel all refreshed. Weird.

Thinking about a web terminal again as somewhere to code during work. Quite tempted to write the server side stuff in C since there's not going to be much of it.

The HTTP side will need to parse requests enough to send static files, but that's only the request line.

Need a select/poll loop for sockets, might as well use sendfile for static files.

I'm thinking about email to blog again, although as a follow up to dealing with a food diary. It's a neat idea in theory, I can send emails from work, my phone, or my desktop/laptop, but the amount of work needed to setup a collation system is similar to (or, probably, more than) that needed to stick up a web form with a database, or add tags to the blog.

The problem I'm trying to solve here is that, as part of a diet, I want to track to food I've eaten. I've got the old calorie count spreadsheet, but it's annoying to add new foods to that, and I'm not actually counting calories this time.

I wrote down yesterday's food in a note on my phone, but I'm not as interested in keeping my phone on me at all times as I was previously.

I'm coming round to the idea that using the blog to log foods isn't a bad idea, although I'll need to collect entries every week for the cult.

I should push the update that fixes edits, move to a database backend, and start thinking about tags.

Looks like the new DB is working. Also fixed the edit bug!

Important caddy configuration tip, make sure you quote values that contain spaces when setting headers, otherwise you get a silent fail.

Tip of the day - you can have HTML button and input widgets outside of a form, so long as you tag the widget with a form attribute with the right id.

(Been waiting a few days to post this, took a little time to get the fix posted).

Uh, oh. Looks like posting might be broken

Nope, posting was fine, but I'd setup the cache wrong. Fixed that now (I think!) so all the dynamic pages are no-store, static pages with a query string (from asp-append-version) get a long (256 days) expiry, and static pages without a query string (which are mostly JavaScript modules loaded through other modules) get a must-revaliate.

I'm not happy about the growing number of headers, it's not doing wonders for the payload:content ratio. On the other hand, at least HTTP/2.0 means that there isn't a fresh connection for each file.

Got up in the middle of last night and did some programming. Fun, except now I'm going be all tired at work.

I think that the thing I miss most from a command line when I'm using a GUI is the command history.

On the command line I can use the up/down arrows to find commands that I've entered before, edit them, and run them again. I can search through the history to see what options I used last time I ran the command, and I could (if I could be bothered) run analysis to find out what commands I use most often.

In the GUI I can't do any of that. Every time I want to do somthing I need to click through a whole pile of menus and dialogs, unless the application authors have included a shortcut for the specific thing I want to do.

I probably could install a mouse/keyboard recording tool, but that still wouldn't have the naked convinience of typing up arrow enter to repeat whatever it was I just did.

Bugger. MouseDown event is not triggered by touch.

What lists does paint need?

Commands, for undo Paths and graphic styles, for paint Paths for object selection

Drawing an object goes from start to commit, although maybe more steps (add point, move anchor).

objects have a selected bool that changes their look (draw anchors)

startObject should render if it's top of the stack, but not otherwise.

Commit could manipulate the undo stack, replacing start with itself, keeping the start command and swapping it back on undo

Other commands can change objects (fill, style). Object stack is different to command stack. Commands create/alter/delete objects, and keep references of the old object and changed properties for undo.

Anchors are not objects, but instead are additions to the paths of selected objects.

Draw runs through object stack oldest first.

Paint todo/ideas

Infinite scroll - there's no reason to limit the drawing area since we're only putting pixels on the screen where the canvas viewport is. Add a global transform to the context with translate and scale (zoom), and a pan tool for click to drag.

Save - stash the action stack and drawing stack somewhere so the canvas restores at page load time

Export - Download current viewport as a png, download everything as svg

Style - Set things like line width, fill/stroke colour. Maybe a properties flyout on the right?

Object select - to edit object properties

If we store paths as svg paths then they'll serialize easier. Doesn't really matter, for rectangle and circle we can store coords and radius and convert them to paths later.

If we're going to do a property editor then we should spec up property types so we can generate the form mechanically. A shape is then defined by a list of property/type pairs, and maybe click handlers to manipulate anchors.

Pants. Serializing the action stack is going to be tricky, since it's a pile of functions (and closures).

One fix is to write a language - based on lox or mal - and have the ui save instructions in those terms.

let r = rectangle(10, 10, 35, 60)
set r lineWidth 2
set r strokeStyle "green"
draw r

// useful for humans, not sure if would ever be generated
set globalStyle {lineWidth: 4, strokeStyle: "brown" }
for (let r = 10; r <= 50; r += 10)
  let c = circle(25, 25, r)
  draw c
done

I don't think we need a full on VM, but a parser to build an ast, and then a visitor to execute - jlox, but in JavaScript and maybe a little stripped down (but also with object literals).

Worth a try, I haven't been through jlox in a while.

(of course, if we don't need humans to be able to read/write it, mal is probably easier to code, maintain, and extend, but I've got a picture in my head of me messing about in a console and that feels easier in lox. Mental note: leave space for pluggable languages)

I'm feeling proper knackered this evening, even after a couple of hours sleep. Did a bunch of thinking today (work and paint), and the diet is starting to kick in.

So yes, lox was/is massive overkill for the paint language. Current design is command per line, with a command word followed by space seperated options. E.g:

draw rectangle 1 10 30 20 40

Currently it knows draw rectangle, draw circle, and erase last (where the erase command is to undo one of the other two). I'm going to need to add at least move, resize, rotate, maybe skew, and set style.

Commands generally take a shape id as a parameter.

We'll need to add more shapes.

Let's make a Drawable a bag of properties - an object - with a type property to inform how to use those properties to e.g. draw a path.

{
  "id": 7,
  "type": "Rectangle",
  "top": 15,
  "left": 10,
  "bottom": 25,
  "right": 20,
  "style": {...}
}

(Somewhere there's a map of names to property types to inform a property editor.)

drawableStack gets renamed to display, push to paint, pop to erase, draw to refresh (and bring the refresh once a frame logic into the type along with getBoundingBox).

Shape specific code (such as drawable to path and drawable to anchors) is held in the shape objects that also implement the tool interface.

Another night staying up late to put off tomorrow. Work has got crap again, I think. Also Trump/America is being stupid again. (And yes, I get that Trump isn't actually stupid in any real sense, but the options he's picking are not great for future humanity)

Paint is going ok, I guess. Need to do colour and fill next, along with adding lines, curves, polygons, and probably resizing.

I've sunk all this time into rectangle and circle, and I'm worried that lines and other shapes are going to be too different for the shared code to help.

I'm too tired for this. See you later blog.

Lego Wishlist for March 2025

Lego.com prices:

  • 60488 Red Sports Car £8.99
  • 60450 Yellow Construction Wheel Loader £12.99
  • 60451 Emergency Ambulance £17.99
  • 71829 Lloyd's Green Forest Dragon £17.99
  • 60461 Red Farm Tractor with Trailer & Sheep £17.99
  • 60453 Lifeguard Beach Rescue Truck £19.99
  • 60439 Space Science Lab £34.99

Alternative (single purchase):

  • 60434 Space Base and Rocket Launchpad £124.99
  • 71822 Source Dragon of Motion £129.99

Oops. Hydra, the OAuth2 backend I'm using, quietly upgraded itself at some point, and that broke login. Fixed now (clearly, or I couldn't post).

Lesson learned: Use a specific version tag on containers (not :latest, or no tag).

Not that expensive a lesson this time at least.

Also, if any of you are keeping track, I got the "Source Dragon of Motion" from Lego (well, from Smyths and saved £20 or so)

Our savings account is held with Nationwide, a UK building society. I've had an account with them for a long time, and I'm generally happy with their services. However, about a week ago I ran into an issue with their android banking app - when I opened the app I got a security error instead of a login page.

Luckily, it only took a phone call to customer services and a trip into town to reset my online account for me to remember that a few weeks back I went through the big list of CAs that android trusts by default, and turned most of them off. (This probably counts as paranoia, as I imagine that government scale naughty people can get their CA trusted without it being listed.)

The certificate for Nationwide's home page is signed by"SSL Corporation", but enabling them in the list didn't help. A closer look at the certificate suggested adding "Entrust Inc." to the permitted list, and yup, the app is working again.

Thoughts

Nationwide are using different CAs for their webpage and their app. I was lucky the two CAs were related enough that I could find the connection, else I would need to either do some kind of search through the installed CAs (by turning them on and off until I found a match), or give up on Nationwide and pick a new savings provider.

I didn't look very hard, but I couldn't find anything through Google that looked like it would tell me which CA the Nationwide app trusts. I didn't bother asking Nationwide customer support, I don't imagine that question would be fun for anyone.

More broadly, banks (and retail in general) don't seem setup to help customers check the CA being used is the right one.

Since the Nationwide app only works if the system CA is enabled, it's likely that the app doesn't have it's own CA, and so can probably be spoofed with a proxy and a user installed CA (or a naughty system CA).

Hello blog!

We're off to town later today to watch the Dr Who season end on a big screen (Tyneside cinema), hopefully that will be a giggle.

I've got a new laptop arriving tomorrow. It's a tiny chromebook that should be easier for me to take round the house/out to places. We'll see.

I want a webhook to trigger a command, without giving the webserver auth to do, well, anything.

Solution: create a ssh keypair, add the public key to the target account authorized keys with exactly the permitted command, and then have the webhook trigger a ssh connect to request the task is fun.

Only the person who holds the ssh private key can trigger the command, and the person holding the private key can only trigger the command. Ideal.

(Implementation note: get the webhook recevier app to create and store the ssh keys, and to show a "copy this into your authorised keys" box somewhere)

Back from watching the new Superman (2025) movie in town, that was a giggle!

I enjoyed the movie. Specifically it had a couple of interesting fight scenes; both were in the background to some degree, one completely (with a Lois/Clark conversation in the foreground), and the other partially, with the main focus on Lois' reaction to the super powered fight happening around her.

There were a few good jokes, not too much hetro propaganda, good music picks (and clever use of music to highlight scenes).

The movie loses a point because I didn't like the portrail of the Kents. I'm used to The Lois and Clark adventures where Martha (especially) and John (somewhat) are both thoughrouly practical people who can take whatever life gives them and come out stronger. The Kents in the movie felt delicate and vounerable (which, I guess next to Clark they are) in a way that felt deeply unsafisfying.

I'm also not sure about the casting for Superman himself. The actor made a good Clark, but Superman felt soft (but Henry Cavel is a hard act to follow).

Taking about casting, Lex Luthor was excelently creepy, nailed the role, and Nathan Fillian was an inspired choice for Guy Gardener (Green Lantern, who is canonically unlikable).

Overall a good movie. Standard "going to the movies" problems (can't pause to go to the loo, can't mute the adverts, can't get proper comfy in the seat) plus the car park under the cinima had opressivly low ceilings and cost £5 for 2-3 hours.

Hello, by the way, to husband. I've just sent you the link to here so please let me know if you get this far (or if you just skipped to the end).

Today's been a good day. Did a bit of sleeping, did a bit of reading (Murderbot, again), did a bunch of programming, I even got out of the house (to pick up catfood).

I've picked up 3d graphics again (now that Firefox supports WebGPU)(at least on Windows so far, although I'm doing a lot of testing in Edge (at work) and in Chrome (on my Chromebook)). I'm working through [articles from a site called web gpu fundamentals] (https://webgpufundamentals.org/) and I'm (hopefully) about to get generated landscapes working.

My food diary app is working well, I've picked up a few weeks worth of data and so the next step with that is to do some analysis (although I'm willing to bet now that most of my points come from hifi bars). I might add weight measurements into it so I can compare points to weight (but husband's analysis of their data suggests no correlation between the two). I'm also thinking about how to handle "one off" foods - say I get a cookie tomorrow, I don't want a "cookie" button cluttering up the screen. Probably have a "visible on front page" toggle for foods, maybe driven by when I last ate something.

I'm still a bit chicken to actually start using the "deploy server changes automatically" system I've been building, but I think that's just paranoia. However, I'll have a think about how I can set up automated testing - at least smoke tests - either before deploy, or with automated rollback on test failure.

It's been a while since I've posted regularly, I have had trouble deciding if this is meant to be public or private (regardless of how many actual humans are reading this). However, choice made, it's a public blog, and so I'll (hopefully!) start posting again on that basis.

Frickin' time zones are broken for the blog as well! (The timezone for sunrise/sunset on the front page is also broken).

I'm do some digging in a bit to check what timezone the server thinks it's in (Rant: I really miss the setup I had in the early 00s where the site was perl cgi (maybe mod_perl, maybe not) and there was a page that loaded the cgi script into a textbox and saved the script back over itself, so I could make changes to the live site through the live site! It's much harder these days with compiled code and build pipelines and sensible levels of paranoia)

On the plus side I've finally put the duvet cover on the duvet (from when I wanted the cover a few months back).

Spent half the day writing a feature for source hut (so I can stash webhook urls as secrets), but I need to test it before I can submit it, and that's going to need a local source hut install, which I wanted to avoid.

But it's not going to be an overwhelming amount of work. I can spin up an Alpine VM, install the source hut packages from their repo and then install my patched package over the top.

(I guess I can even check that my feature is self hosting)

Still. I've been grumpy all afternoon. I should be able to just throw code at these people! Damn them and their entirely reasonable standards!

In other news I've seen, and liked, the first couple of episodes of Ironheart (2025), a Marvel TV show about a genius mechanic (who uses it to do crime rather than solve crime).

(Crap, energy crash. See you later blog)

OMG! I'm so awesome! Over at my WebGPU page I'm successfully rendering a generated landscape! (Or, at least, I am tonight, who knows what state it will be when you're reading this) (hello you!)

The landscape is basic diamond-square, and the rendering code is lightly adapted from WebGpu Fundementals (a very useful tutorial site that's pitched at exactly my level of understanding).

I'm using "build a game" as an excuse for all this 3D stuff, but I'm still not convinced. However, I'm going to roughly follow Trystan's Roguelike Tutorial, at least in terms of order of doing things and maybe rough mechanics.

That implies getting user input working (starting with keyboard, but I'd like to do mouse interaction too), and then the whole actors thing.

As far as graphics go, I'm going to look at Phong shading next, design some trees, update lighting, think about a sky box, overlay an html ui.

The code needs a bit of a tidy too. Right now, for obvious reasons, the graphics stuff is front and center. That needs be properly put into its own module (and maybe I should be thinking of 'components' and Entity Component Systems).

However. That's all later. Right now, whooop! It's working!

Given a triangle in 3d space ABC, we know it's normal is

n =(B-A)x(C-A)

We also know that a point r in the plane of the triangle satifies

n.(r - A) = 0

Since we know two of the coordinates of r, we can rearrange to calculate the missing third coordinate.

Expand the brackets

n.r - n.A = 0

Add n.A to both sides

n.r = n.A

Expand left hand dot product

nx*rx + ny*ry + nz*rz = n.A

And rearrange to get rz

nz*rz = n.A cm - nx*r - ny*ry

rz =  (n.A - nx*rx - ny*ry)/nz

Backup script is nearly working, except it's changing the permissions/ownership of db files. Which breaks the blog. Which has cost me at least one entry. Poot.

If I want to create a container for running automated tests, I need to give it databases in a known state. I can use dotnet ef to create empty databases (TODO: Look up how to seed a database without creating a migration, or at least make it optional. I can use SQL scripts to populate most of my databases, but creating a password for a test user is a bugger).

Alright, it's the password that's the main blocker. Options:

  • create a password using the ui (once) and copy it out of the db. Fiddly, but only once.
  • Use the spectre cli stuff to add a create password/user mode to the app.

That second option I like a lot. Let's do that.

Hello world, do you like the new look? I think I'm a fan.

I've stripped out almost all of the css, only putting back enough to try to pin the nav menu to the top of the page. I'm only using system colours and default fonts, and I added a few lines to the blog archive that means the month tables wrap.

(Oh, yeah. I replaced the css grid month tables with, y'know, actual tables. Seriously, css grid is fun, but calendars are close to ideal table data.)

This blog entry box needs a "max-width: page-width", but I'll deal with that later. I've just come back from town and gosh, that place is hard to cope with!

On the plus side, I came back with new XL trousers, a significant improvement over the 5X trousers I bought last time. I tried L, but they were a little tight. Maybe in a month or two.

Think I've got the fixed menu working now, thanks to a stack overflow answer I'm using box-shadow to draw outside the box of the element, so I can keep it at 8px from the top (standard body margin) and still make those 8px opaque.

I wonder when CSS is going to get references, so I can do background-color: ref(body.background-color); (yes, I could do something similar with variables, but I don't want to have to specify everything, I want to use whatever the user (browser) thinks is default.

Ah, well.

In tonight's episode of "Fun with pipelines", we've got this monstrosity:

awk '{ print $3 }' logs/* |  
	sort -n |
	uniq |
	tail +6 |
	awk '{print "-x " $1 }' |
	xargs dig +noall +answer |
	rev |
	awk -F. '{print $2 "." $3}' |
	rev |
	sort |
	uniq -c |
	sort -nr |
	head -n 10

which produces an output something like:

    349 amazonbot.amazon
    186 ahrefs.net
     67 yandex.com
     47 msn.com
     39 semrush.com
     16 petalsearch.com
     14 babbar.eu
     10 googleusercontent.com
     10 google.com

telling us that amazon used nearly 350 different source IPs to make requests to this server.

Explanation

It's bash, so the | at the ends of the lines means "pass the output of this command to the next command".

awk '{ print $3 }' logs/*

Awk is a small text processing tool. By default, it splits its input into lines (using \n) and then into fields (using spaces and tabs). In this case, print $3 means "print the 3rd (counting from 1) field". We've told awk to read all the files in the logs folder, which have an IP address in the 3rd field.

sort -u

sort takes it's input, splits into lines, sorts it, and prints the output. The -u option tells sort to only output one copy of identical lines (which is what we really want, the sorting is a side effect).

awk '{print "-x " $1 }'

Awk again, this time printing each input line with "-x " in front, which helps us build the next command.

xargs dig +noall +answer

xargs is a neat toy. It takes the input fed to it, and the command you give it, and then it runs the command, passing the command all the input as options.

It's very useful for, well, this kind of situation, where we've got a list of things that we want to run a command with.

The command we've asked it to run is dig, a "DNS lookup utility". Given a hostname, it will find the IP, or (as in our case), given an IP address, it will find the hostname. We have to give it the -x option per address to tell it we're looking for the name. the +noall +answer options limit it's output to the part we care about (the answer!).

rev

rev outputs each line of input reversed.

(Sorry, I ran out of energy at this point. Hopefully I'll come back and finish at some point)

This input box is still too wide, but not by much.

The sticky header isn't on the bookmarks index, although it has picked up the color scheme. I need to pull in the bookmarks database from live to do some proper testing.

Some blog entries have got code blocks wider than the screen, but the browser zooms into fit-to-screen anyway. This breaks the sticky header because it sticks to the page, not the zoomed in viewport the browser uses.

I should go though and make sure all pages have a sensible title. (This new blog page doesn't, for example).

Dammit, I'm all tired and got and grumpy. I haven't even warn my new trousers yet. Going into town knackered me, then I sunk a bunch of energy info the post above about the stupid command line, and it's not like my expected audience actually cares! (Hello babe, I still love you!)

::blows raspberyy::

Right, it's the same problem on the bookmarks page, something is very wide, this makes the browser draw the viewport and the page at different sizes. Chaos ensues.

Different fixes, probably. I'll tell bookmarks to wrap, and I'll tell code blocks to scroll. Tomorrow. I should try sleeping now.

Idea: Give each bookmark a link to fetch.

Also, think of a way of adding a "view source" link to the bottom of all pages, without opening the fetcher to randoms Probably something like only allowing relative links for not logged in connections.

Quietish day, I installed elastic agent on a bunch of machines and then was bug hunting for the rest of the day.

After work (and a sleep, and today's episode of Only Connect) I added some noise into the URL of one of the sites I maintain to stop scrapers from finding it. I should probably just turn on auth, but the site has exactly three users, and it's far less effort to give them a slightly tweaked url, and since the site isn't linked from anywhere else, it shouldn't get found.

(The site does have a TLS certificate from let's encrypt, so the existence of the site is public information, but so long as the root returns a 404, nobody should stumble across the actual content. Unless Facebook strips urls from messenger, for example).

I freely admit this is stupid, but this year's pay offer is out, and it's fine, no worries, except my pay is going to be £1 short of £50k!

(I said it's stupid, yeah?)

Today's pro tip: Do not uninstall the wireless network package that enables the wireless network you are using to reach the machine.

Now I've got "reinstall OS" on the todo list. (For two raspberry pis that I was being all efficient and setting up with ansible.)

Sigh.

I'm getting k3s fettled up again. I've got a VM (tayet) as the server, and a couple of fallow Raspberry Pis as agent nodes. Still not sure what I want to do with a k3s cluster. There's a bunch of stuff to install like step-ca that are services one should have running in your cluster, but I'm working on folding my website down to a monolith, so I don't have that many actual applications.

On the other hand, there is a reasonable chance that we're going to get fast symetric fibre in a couple of months (when the BT contract ends), I'm tempted to bring most of my hosting back home, on principal. Of course, I still need a cloud endpoint for the reputation, and splitting services between cloud and house makes them both slower and less reliable.

Sigh, again. 20 year old me would have gone crazy at the idea of symetric gigabit fibre to the home, but I think husband may be right and we just don't need that kind of bandwidth.

Just got information:

  • My website (that you should be reading this blog on), also has food diary, unfinished bookmark sync toy, and a couple of other projects
  • Webmail (the main excuse for a cloud server, includes a SMTP server, an IMAP server, a few anti spam servers, as well as the website. Oh, and a bunch of storage for emails)
  • Husband's website
  • Private file share, for files to big to reliably email (that we're renting extra storage for)
  • Private container repo
  • Private Nuget repo (That I'm not using any more, I gave up and pulled the packages (and the microsetvices that used them) into a monorepo with the webserver)
  • Oauth2.0 server

Want to add:

  • Metrics/Logs/Traces/Alerts (Prometheus/Grafana used to be my go to choice, but I've soured on them and I don't know why. I'm still looking for something tiny and reliable. I miss MMRTG)

Dealing with the urge to eat a whole bunch of hifi bars. I very much want to, except I also want to get this stupid diet finished (and yes, I do know that the diet never actually finishes. If you're going to be like that, I also know that eating junk doesn't actually help me feel better).

Not feeling the urge/capacity to write much at the moment. I've done a much of tech stuff to the website, depression/lethargy continue to add suck to my life, my body weight is still dropping, life goes on.

New idea: HTTarPit

Listen to a socket, accept connections, wrap the socket in TLS and send the right certificate (use a library for this bit). Use poll to manage sockets, don't need to keep state. While a socket is open, read and drop incoming bytes, write an outgoing space every 20 seconds.

Problem is, looking at the spec, won't work. Http response has to start "HTTP-version SP status-code SP" RFC 9112 Sec 4, so we'll need to keep at least enough state to send that first, although a 13 byte buffer isn't that much.

Not sure what status code to use, but making it configurable solves that problem in the short term.

Sounds like a fun C programming exercise, a fairly classic use of poll that should be able to run under fairly restricted resources and might end up slightly annoying a bot operator.

MVP: Get it working.

Post MVP: Record (and report!) stats. Total connections, bytes received/sent, time connections held open (min/max/average, maybe a histogram once it's clear what size buckets to use). If I wanted to put the time into parsing the HTTP request I could gather stats from headers (UserAgent mostly), and maybe even respond properly to specific URLs (/robots.txt, and maybe something human readable for /), but that significantly increases the complexity and attack surface. But it would be fun.

I hope you all like the new aesthetic for the blog. Minimal, yes? But still a bit expressive.

Something like that, anyway.

Another tooth crumbled, it's the one that was/is half-way through a root canal (started a few months ago, scheduled to be completed end of September). The dentist has slapped in another temp filling, but still, ouch, at least a little bit.

Oh, in technical news, all* pages from this site should be minimized! That's the (generated) HTML and any inline CSS/JS.

*Pages that the minifier can't parse don't get squished

I've written a tag helper that runs the content of any <html> tags through NUglify. (I was already using NUglify to minimize JavaScript files, any .min.js file gets squashed (and cached) on the way out).

It was a little bit of a journey. I wasn't happy with the previous look of the site, so I removed all the CSS. That didn't last very long, but instead of adding <link rel=stylesheet> tags back, I added <style> tags inline in the header, on the grounds that I don't want a lot of CSS, even if I need some, and keeping inline is faster/more efficient than another round trip.

(All the "speed up your site" posts say one should include enough CSS to render the page inline)

However, even a small amount of CSS leads to much whitespace, and I was feeling twitchy. I wrote a quick MinifyCssTagHelper that looked for <style> tags to minify, and that worked surprisingly well, surprisingly quickly. Given how well it worked, it was an obvious next step to try HTML minification, and yeah, that worked to.

It doesn't save much (traditionally, turning on response compression gives far more savings than minification), but it saves something, and it's really cool.

Hello world!

That's been a good long weekend of coding (and that's not counting tomorrows bank holiday!).

Mostly it's been little bits here and there, but there have been lots of them, and I'm much happier with the look of the site now.

  • I've added fieldset tags around most things (those are the boxes with titles)
  • I've tided up/added sub menus to the various areas around the site, and I've got a working version of how to automate them (except maybe blog, which is complicated)
  • I've got a working demo of a drag to reorder module (need to think about no-script versions, maybe)
  • Calendars (on the front page and the blog archive) all now have Monday as the first day of the week! (At friggin' last!)
  • Generated pages (so, like, most of them) are minified on the way out (html as well as inline css and JavaScript) (I should make sure that all the js links are .min.js links)
  • Drawing server side graphs! (Don't think any of those are on the public side yet, but I've also ...)
  • Started collecting http request logs into a database (...so I should be able to sort out an activity graph sooner or later)
  • Adds the skeleton of per user settings (eg, I've put my target weight in and now my weight history gives me an estimated time to target) (33 weeks as of Thursdays weigh in)

Thinking about authorisation ("we know who you are, we're trying to decide if you can do the thing you want to do").

We've got resources to protect - routes that should only work for, and parts of pages that should only display for authorised people.

We've got a list of users (two people is a list!). We can assign arbitrary properties to people including 'roles'. (Roles are magic strings)

I know I should think in terms of "0, 1, lots", but I'm pretty confident that it really is only ever going to me be and husband here, so having three sets of roles ("policies"), one for me, one for them, and one for everyone else shouldn't be real problem.

(I guess I can always just stick it in the database anyway).

New service idea, a POST endpoint that accepts and stores any data, but the path is a token (either a guid or a random word song) that's got various validity checks (can only be used n times, max (and min?) file size, expected content type, source ip, that kind of thing). File gets saved and only the logged in user who created the token can access it.

specific use case is too give Google somewhere to post the data from husbands food diary spreadsheet export, but might be useful to have laying about.

The post entry button is in the wrong place (should be much closer to the input form) and page width is broken again.

Whoop! Achievement unlocked: More than a month's salary in savings!

I'm trying to work out why I want to containerize webmail. Current thoughts:

  • All the cool kids are doing it. Irritatingly possible
  • Easier to keep config version controlled
  • Easier to wipe and reinstall the server

Components (things to containerize):

  • Postfix (SMTP)
  • Dovecot (IMAP)
  • Spamd
  • Spampdq
  • OpenDKIM
  • Webmail

To remember your current position in the blog, this page must store some data in this browser.

Are you OK with that?