Alpha-27 is a mixed release: a meaningful security hardening across the HTTP layer (CSRF enforcement, security headers on all routes), a Redis serialisation fix that has been silently corrupting cached java.time values, and a set of developer-experience improvements around column widths, error codes, and migration safety.
Security: CSRF protection now enforced
CSRF tokens are now validated for session-authenticated, state-changing requests (POST/PUT/DELETE/PATCH). The previous implementation was a stub that always passed.
Tokens are signed, session-bound double-submit values.
The shared page layout already emits a <meta name="csrf-token"> tag and a global htmx:configRequest listener attaches X-CSRF-Token to every HTMX request automatically — existing HTMX actions need no changes.
Plain <form method=post> forms add a hidden field via (boundary.platform.core.csrf/hidden-field).
Configured under :boundary/http :security :csrf {:enabled? :secret :exempt-paths}.
The secret falls back to JWT_SECRET; list webhooks and callbacks under :exempt-paths (trailing /* matches by path-segment prefix).
As part of the same work, the :no-doc route flag no longer skips the default HTTP interceptor stack.
Interceptor application is now controlled by an explicit :skip-interceptors? flag, reserved for genuinely internal endpoints like health checks.
The visible effect: HTML pages now carry the security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options) they were silently missing before.
The ring/ring-anti-forgery dependency has been replaced with buddy/buddy-core; CSRF tokens are generated and verified directly (HMAC-SHA256, constant-time) rather than via Ring’s session-backed middleware, which did not fit the framework’s cookie/header session model.
Fixed: Redis cache serialises with Nippy
The Redis adapter now serialises values with Nippy instead of JSON, fixing class java.lang.String cannot be cast to class java.time.temporal.Temporal for cached java.time values.
JSON was lossy — Temporal values became ISO-8601 strings, keywords became strings, sets became vectors — and the loss only surfaced against Redis since the in-memory adapter stores values by reference.
Nippy round-trips keywords, sets, ratios, and all java.time/Temporal values intact.
The adapter also treats unreadable entries (values written by the old JSON format, or otherwise non-Nippy bytes) as a cache miss rather than throwing, so the cache self-heals on rollout. Flushing the cache namespace on deploy is still recommended to avoid log noise from stale reads.
Added: Proportional column widths in admin list views
Admin list-view tables now derive column widths from field :type and a field-name heuristic, replacing the previous even distribution where a boolean column got the same width as a description column.
Weight table: boolean=1, enum/numeric/uuid/json=2, date/instant=3, string=3 (default), name-like fields (name, title, email, …)=4, long-form fields (description, address, comment, …)=6.
An optional :width key on a field config overrides the computed default.
Widths are emitted as proportional width:N% on the table <colgroup> and resolved deterministically at render time.
Added: AI-suggested :width in generated admin entity configs
bb ai admin-entity now suggests :width values in generated EDN configs, completing the column-width system.
The AI is taught the same type/name weight table used by the runtime heuristic and only emits :width for fields whose semantics differ from the default — e.g. :sku, :code, :ref, :barcode get {:width 1} while :description and :name are omitted because the heuristic already assigns them the correct weight.
No AI on the hot render path.
Added: Unified error-code catalogue in devtools
bb guide error BND-201 and bb guide error BND-601 now return title, cause, and fix instead of "Unknown error code".
error_catalog.edn (libs/devtools/resources/) is the single source of truth consumed by both the JVM runtime (boundary.devtools.error-codes) and the Babashka CLI (bb guide).
BND-0xx tooling codes are retired in favour of a range scheme: BND-1xx configuration, BND-2xx validation, BND-3xx persistence, BND-4xx auth, BND-5xx interceptor, BND-6xx FC/IS violations, BND-7xx tooling/build (circular deps BND-701, admin entity config BND-702, module not wired BND-703, migration version conflict BND-305).
bb guide error with no code lists all codes grouped by category.
Added: Migration subdirectory warning
discover-migration-dirs now scans each resolved migration directory and emits a WARN log for any subdirectory that contains .sql files.
This catches the class of misconfiguration where tenant-scoped migrations are placed inside the public migration root and silently applied to the wrong schema — the warning fires on the first clojure -M:migrate up run rather than producing a hard-to-diagnose data error later.
Version alignment
All libraries bumped to v1.0.1-alpha-27 to maintain lockstep versioning.
Upgrade
Re-run the installer to pick up the latest release:
curl -fsSL https://get.boundary-app.org | bash
No breaking API changes in this release.
CSRF enforcement is new behaviour — review your :exempt-paths config for any webhook or callback endpoints before upgrading.
Flush the Redis cache namespace after deploy to clear stale JSON-encoded entries and silence the self-heal log noise.