I've added libvips as a third option for thumbnail generation. It's better than ImageMagick in that it is around 4x faster, and uses 1/10th the memory. It is particularly good if your webserver has more than one core available as it will split the work across multiple threads, whereas ImageMagick won't. Like ImageMagick, it is colour space aware (GD isn't).

In terms of speed and quality, the options are now (best to worst):

  • libvips
  • ImageMagick
  • GD (default)

GD is best avoided, and is the default only by virtue of being widely available on most web hosts, whereas ImageMagick is sometimes available, usually with some configuration, and libvips usually isn't a PHP thing.

To enable it, install libvips-tools on your system and rename ResizeImage-Vips.php to ResizeImage.php (and backup or remove the existing one). Refer to the files's docblock for instructions, and to configure compression and sharpening.

A SCP story. High quality production. Well worth 15 minutes of your time. Published on DUST | A Rose on Thorne Street.

This is part 3 of the series "Strange anomaly in the deep woods". Story by Dougie Corrado.

A small update to simplify / reduce friction during installation:

  • Change index.php to route from request path only, independent of scheme and host. This avoids the need to manually configure when deploying behind reverse proxy that handles TLS termination.

A minor update that provides a performance boost, file structure reorganisation and security hardening:

  • Optimise database performance: Indexes have been added, which accelerate queries, improve resilience under load, and further reduce the already small hardware requirements of Tuskfish. The indexes are primarily of benefit to sites with large content loads.
  • Modularity has been improved so that new modules can be dropped in or ripped out without modifying other code, which will simplify future development.
  • The SMTP password is now encrypted at rest.

Whenever I think Tuskfish is 'finished' some new ideas will hit me. I do think we are in the endgame now, here is what's planned:

  • Optimise database performance [done]: Add indexes and improve a couple of query issues. This is being tested on my largest production site, which has > 1,000 articles. Page execution times, which were already fast, have been halved, with individual content pages now down to 5-8 ms, while the index page (heaviest) is down to 10-12 ms. Practically speaking, you probably won't notice any difference unless you have a massive site. But halving execution times means your site can handle (nearly) double the number of requests and makes it a lot more resilient under load.
  • Complete modularisation [done]: I would like it to be able to add new functionality by dropping a single directory into the system and not having to touch anything else. It's mostly there but there are still a couple of things you need to manually wire in (module header, templates).
  • Dockerise Tuskfish: Create a Docker Compose stack and Makefile that will let you deploy Tuskfish with a single command. I've done this for a couple of other projects recently. Imagine typing 'make up' and the whole system just builds itself and deploys inclusive of self-updating TLS certificates.

Then, I think I need to go work on something else for a while.

I'm releasing Go2Serve, a static file server written in Go. It's a single binary with simple configuration, no runtime dependencies, and secure defaults out of the box. You can build it and learn how to use it in two minutes.

I built Go2Serve because I wanted something lightweight, performant and safe. Something I could drop onto a Pi or a VPS, point at a directory, and have it serving files over HTTPS in under a minute with near-zero configuration.

Go2Serve serves static files. That's it. No CGI, no reverse proxying, no dynamic content. What it does do, it tries to do well:

  • HTTPS with zero configuration: Pass `--domain example.com` and go2serve handles Let's Encrypt certificates automatically, including renewal. Manual certificates are also supported, with automatic reload every 60 seconds for zero-downtime rotation.
  • Security defaults: Path traversal protection (including via symlinks), `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, and optional HSTS and Content-Security-Policy headers. These are on by default, not buried in a config file you have to remember to write.
  • Per-IP rate limiting: Token bucket rate limiting is enabled out of the box, with proxy-aware client IP extraction when you're behind a load balancer.
  • Lightweight: No CGO, no runtime dependencies. The Docker image is built from `scratch` and contains nothing but the binary and CA certificates. Memory footprint is minimal.

Go2Serve ships Docker-first. The supported, cross-platform install path is "make up", which builds a pinned, reproducible Linux image and runs it in a hardened scratch container — identical on Linux, macOS, and Windows (WSL2). Building a native binary (make build-bin, requires Go) is offered as a secondary path for non-Docker hosts. See the README.md!

A minor feature and security hardening release:

  • Add SMTP support via PHPMailer.
  • Add TFISH_EMAIL_URL constant to config.php.
  • Proactive security hardening pass based on sweep by Claude Opus 4.6.

[Update: This is now done] Just a quick note about the next release (2.2.9) of Tuskfish:

  • Given the proliferation of AI-assisted attacks on software and supply chain ecosystems, I wanted to let people know that the Tuskfish code base has been proactively put through several rounds of security scanning and a structured evaluation by a strong AI model (Claude Code Opus 4.6). No serious issues were found, and to the best of my knowledge Tuskfish 2.2.7 is safe for production use.
  • A few minor issues were found, which basically concern additional hardening, adding defense in depth and tidying up. These have now been patched and will be released in v2.2.9 sometime in the next week once I've had a chance to test them. You can grab them from main right now, if you like, but I suggest you wait.
  • Additional evaluations are planned (not yet done) using a different model (Codex) will be conducted periodically as stronger models become available. So: We're not done with this, evaluations will become part of the process as new and stronger models become available.
  • One new feature: Support for SMTP mail has been added, and I have ditched the native mail() function of PHP, which should make it easier to get email notifications up and running.

Coming soon: I will be developing a Docker Compose package that will allow automated deployment of Tuskfish with a one line command. I just did this for a new project and wow it just makes life so much easier.

TLDR: You can get great results but it requires a structured review process. If you are just "vibe coding"  you are building a bomb that is going to go off in your face.

I've been experimenting with AI code generation for a side project written in Golang. The project has been implemented by Opus 4.6 (Claude Code) under my direction. This is the first time I've used Golang so I'm pretty slow and can't scrutinise the output as thoroughly as I could PHP. I've been thinking a lot about security. Are there processes we can follow to reduce risk, when working with machine-generated code? I think so. My high-level process has been to:

  • Have a discussion with the model about a feature or a change, to identify a good approach. It often comes up with better ideas or refinements.
  • Explicitly ask for an implementation plan, causing the model to break up the problem into a structured series of small steps, which I sanity check (read) and adjust.
  • Ask the model to implement the plan (if it is complex, perhaps one phase at a time).
  • Manually check that the change functions as expected.
  • Explicitly ask the model to review the changes and evaluate if it is a robust solution (repeat if necessary).

This process works well for two reasons. Firstly, it breaks up the work into small, carefully scoped chunks that fit within the model's context window, keeping it focussed. Secondly, the review aspects (the manual check, and instruction to critically review the work) removes a lot of bugs, so you maintain a solid foundation to work from. Most of the time Opus will find a few bugs in its implementation, if you ask it to check, and it may take two or three rounds before it stops finding problems.