Commits


migrate off github


Portability: Add shims for timingsafe_bcmp, arc4random_buf, and timegm


Cleanup: Remove unused database queries and add strsep shim


Batch safe characters in cgi_html_escape with fwrite Instead of calling fputc for each non-special character, track runs of safe characters and emit them in a single fwrite call. Reduces function call overhead for typical alphanumeric inputs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Add index on bookings(day) for day-specific queries The existing UNIQUE indexes have (desk, day) and (user, day) as their column order, so queries that filter by day alone cannot seek directly. This index lets the day bookings and LEFT JOIN queries use an index scan instead of a full table scan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Combine bookingform's two queries into a single LEFT JOIN The booking form page previously ran separate queries for day bookings and available desks, each requiring its own prepare/bind/step/finalize cycle. A single LEFT JOIN query returns all desks with their booking status in one pass, halving the SQLite overhead for this route. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Use stack buffer for URI path copy instead of strdup CGI request URIs are short (the longest valid path is /book/YYYY-MM-DD at 16 characters). A 256-byte stack buffer avoids one heap allocation per request. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Use stack buffer for POST body instead of heap allocation MAX_BODY is 4096, well within stack limits. The content_length check already ensures the read will not overflow. This avoids a malloc/free pair on every POST request. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Remove -lpthread from default LDFLAGS deskd is single-threaded. On macOS the shared SQLite library resolves pthread symbols through its own linkage. On OpenBSD with static linking the linker pulls in only the symbols that SQLite actually references; if the static build fails, restore -lpthread via the LDFLAGS override. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Remove unused -lm from LDFLAGS No application code uses math.h functions. On a static build the linker would only pull in referenced symbols, but removing the flag avoids unnecessary library scanning during the link step. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Use hex lookup table instead of snprintf for CSRF token encoding Replace 16 snprintf("%02x") calls with direct table lookups. snprintf parses the format string on every iteration; the lookup table converts each byte to two hex characters with simple shifts and masks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Enable -Os optimization for smaller binary output The default optimization level (-O0) leaves dead code and misses size-reducing transformations. -Os is preferred over -O2 as it avoids aggressive inlining and loop unrolling that inflate the binary, which matters for a statically linked CGI deployment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Buffer stdout to reduce write(2) syscalls per CGI response CGI route handlers make dozens of small printf/fputs calls. Under the CGI model stdout is piped to the web server and line-buffered by default, so each newline triggers a separate write(2) syscall. Switching to full buffering batches the entire response into one or two writes that flush when the process exits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Redirect unknown routes to home page instead of returning 404 Users with old bookmarks or shortcuts were getting a plain-text 404 error after d3b734c. Use a 302 temporary redirect to / so they land on the bookings page instead, without permanently caching the redirect. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Unveil /dev/urandom and SQLITE_TMPDIR for SQLite on OpenBSD SQLite internally opens /dev/urandom to seed its PRNG and searches /var/tmp, /usr/tmp, /tmp for temporary files. Both are blocked by the unveil sandbox, producing spurious accounting log entries. Unveil /dev/urandom unconditionally so SQLite gets proper entropy instead of falling back to time()+getpid(). For temp files, unveil the path in SQLITE_TMPDIR when set, letting deployers point it at the database directory already inside the chroot. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Modernize C style: declarations at point-of-use and const qualifiers Move variable declarations from top-of-block to point of first use (C99/C23 style), add const qualifiers to locals and value parameters where appropriate, and remove #include directives made redundant by transitive includes through project headers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Replace vmactions/openbsd-vm with cross-platform-actions/action vmactions/openbsd-vm has a known reliability issue (vmactions/openbsd-vm#11) where the SSH connection to the VM drops mid-test, causing SIGPIPE failures. cross-platform-actions/action is faster and more stable. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Refactor routing to branch on path first, then method The flat if/else chain checked (method, path) pairs and required a separate block that re-enumerated every known path for the 405 case. Restructure as nested branches: outer level matches path, inner level matches method with a per-path else → 405 fallback. This makes each route self-contained so new paths cannot accidentally miss the 405 case. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Return 404 for unknown routes and 405 for unsupported methods The catch-all in the request router previously returned 400 for both unknown paths and unsupported methods. Split it into two branches: known paths with the wrong method now return 405 Method Not Allowed, and unrecognised paths return 404 Not Found. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Update routing tests to expect correct HTTP status codes Change route-unknown to expect 404 Not Found instead of 400, and route-wrong-method to expect 405 Method Not Allowed instead of 400. These tests will fail until the routing logic in deskd.c is updated to distinguish unknown paths from unsupported methods. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Add 12 missing integration test cases Cover previously untested code paths: routing catch-all (unknown routes, wrong methods), missing CSRF cookie (vs mismatch), missing day parameter in cancel form, empty request body on book, cancel of non-existent booking, past-booking filtering on the bookings page, empty day query parameter on dateform, no-desks-configured edge case on bookingform, and HTML escaping of desk names and usernames to guard against XSS regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Statically link deskd for chroot deployment The binary runs inside an OpenBSD httpd chroot with no access to shared libraries. Add -static to the default link flags via a LDFLAGS_STATIC variable that can be overridden (e.g. LDFLAGS_STATIC= on macOS where static linking is not supported). SQLite's static library depends on pthread and math, so add -lpthread and -lm to the link flags. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Add make test recipe and simplify CI to use it Add a test target that depends on the deskd binary and runs the integration test suite. Simplify the CI workflow to just make test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Update actions/checkout to v6 v4 uses Node.js 20 which is deprecated on GitHub Actions runners and will be forced to Node.js 24 starting June 2026. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>


Document unveil and pledge rationale in main() Expand comments to explain why the database parent directory is unveiled rather than the file itself, why unveil(NULL, NULL) locks the path list, and why fattr is included in the pledge promises. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>