OpenBSD has lots of version numbers, each incremented at their own pace and for their own reasons. Here’s a rundown.

The OpenBSD version number, the number you’re probably most familiar with, is incremented by 0.1 every six months. This increment happens regardless of the changes made. Every release adds some new features, fixes some old bugs, probably adds a new bug or two, and, if I have anything to say about it, removes some old features. The six month release cycle has priority over development, meaning if a feature isn’t ready, it waits for the release after; the next release doesn’t wait for the feature.

Pretty much anything can change between versions. The kernel interface (more on this later) can change, such that software developed for 5.4 won’t run on 5.5. The behavior of command line tools can change. The format or organization of config files in /etc (pf.conf) can change. The existence or absence of third party software like nginx or sendmail can change. Numbers are decimal; after X.9 they roll over to Y.0 without fanfare. Perhaps the only time that version change seemed significant was 3.0, with the coincidental introduction of pf. But 4.0 and 5.0 were probably less remarkable.

There are no point releases. Errata are published as needed, but they are simply identified as patch 1, patch 2, etc. The release number may have a postfix modifier such as -stable, -current, or -beta. The current marker is applied immediately after a release, before switching over to beta with an incremented version number. 5.5 becomes 5.5-current becomes 5.6-beta becomes 5.6. Because the largest and riskiest changes are often committed at the beginning of the release cycle to allow for more testing, for most of its life 5.5-current will have more in common with 5.6 than 5.5.

Kernels also have a build number: OpenBSD 5.5-current (GENERIC.MP) #12 . That last number means this is the twelfth kernel built since running config. See the /sys/conf/newvers.sh script. This number is a archaic remnant of long ago. It is mostly useless. For instance, a bug report that specifies “kernel number 27” means little.

Libraries within OpenBSD are versioned using a simple scheme. Every time a function is added, the minor goes up by one. Every time a function is deleted, the major goes up by one (and minor is reset to 0). Changes are batched to prevent racing the number too much, but it’s not tied to the release number. The major of libc is currently 77. Symbols within libraries are not versioned.

Packages usually follow the numbering scheme of whatever their upstream publishes. When patches or other changes are made to the port (even to adjust the description), a prefix like p0 or p1 is added to indicate the patch level. Ports also try to impose the same versioning scheme as base libraries on shared libraries within ports. The special quirks pkg helps handle situations where upstream version numbers don’t always advance as expected (perhaps because the successor package was renamed).

Some things do not have version numbers. Returning to the kernel, several interfaces aren’t versioned. The set of available system calls changes from time to time. For releases, this is detectable via release version number, but that’s not possible for current development. Most system calls show up in libc, leading to version changes there, but that’s not always true. A new system call is typically added about two weeks before userland starts using it. Old system calls are supported via COMPAT options for up to one year in most cases, when it’s feasible. Exceptional cases like the 64 bit time_t rototill resulted in a lot more change and a lot less backwards compatibility.

New sysctl interfaces are usually added, leaving old interfaces for compatibility for a few releases. The ioctl interface follows similar rules. However, because structs internal to the kernel are often used in these interfaces, sometimes existing systls or ioctls will break; e.g. the size of struct pf_rule may change to accomodate a new feature. Mismatched kernels and pfctl will not work. Same for anything using libkvm. The good news is that for common sysctls, like the ones that return information about processes, special structs were created to isolate the interface from kernel internals. The structures that you care most about (and also the ones that change the most) have stable interfaces.