Liking cljdoc? Tell your friends :D

platform

Infrastructure layer for HTTP routing/interceptors, database integration, CLI/runtime wiring, and shared platform adapters used by feature libraries.

Key namespaces

NamespacePurpose

boundary.platform.shell.http.interceptors

HTTP interceptor composition and execution

boundary.platform.shell.interfaces.http.routes

Normalized route handling and dispatch

boundary.platform.shell.adapters.database.*

Database setup, context, and shared persistence infrastructure

HTTP interceptors

Declarative cross-cutting concerns (auth, rate limiting, audit):

;; Define an interceptor
(def require-admin
  {:name  :require-admin
   :enter (fn [ctx]
            (if (admin? (get-in ctx [:request :session :user]))
              ctx
              ;; To short-circuit you MUST set :halt? true — setting :response
              ;; alone does not stop the pipeline, so the downstream handler
              ;; would run and overwrite this response. See "Interceptor phases".
              (assoc ctx :halt? true
                         :response {:status 403 :body {:error "Forbidden"}})))
   :leave (fn [ctx] ctx)   ; optional response processing
   :error (fn [ctx err]    ; optional error handling
            (assoc ctx :response {:status 500 :body {:error "Internal error"}}))})

;; Attach interceptors to routes
[{:path "/api/admin"
  :methods {:post {:handler     'handlers/create-resource
                   :interceptors ['auth/require-admin
                                  'audit/log-action]
                   :summary "Create admin resource"}}}]

Built-in interceptors

The default HTTP stack (boundary.platform.shell.http.interceptors/default-http-interceptors) is applied to every route unless the route opts out with :skip-interceptors? true (used only for internal endpoints such as health checks). In :enter/:leave order:

  • http-request-logging — request entry/completion logging with timing

  • http-request-metrics — timing and status-code metrics

  • http-error-reporting — captures exceptions to the error-tracking service

  • http-correlation-header — adds X-Correlation-ID to the response

  • http-csrf-protection — validates/issues CSRF tokens (see CSRF protection)

  • http-security-headers — CSP, HSTS, X-Frame-Options, X-Content-Type-Options, …

  • http-error-handler — maps an exception’s :type to an HTTP status

http-rate-limit (fixed-window, optionally Redis-backed) is available to attach per route.

:skip-interceptors? is independent of :no-doc. :no-doc only excludes a route from the Swagger spec; it does not affect interceptors. All /web routes are :no-doc yet still run the full stack so that security interceptors apply to the UI.

Interceptor phases

  • :enter — request processing (auth, validation, transformation)

  • :leave — response processing (audit, metrics, transformation)

  • :error — exception handling (custom error responses)

Enter phases run in order; leave phases run in reverse order.

To reject a request from :enter, set :halt? true in the context (alongside the :response). The pipeline short-circuits only on :halt? — setting :response by itself does not stop forward execution, so a later interceptor or the route handler would overwrite it. :leave still runs for interceptors that already executed.

CSRF protection

http-csrf-protection enforces CSRF for session-authenticated, state-changing requests and issues tokens for rendering. Pure token functions live in boundary.platform.core.csrf.

A state-changing request (POST/PUT/DELETE/PATCH) is validated — 403 on a missing or invalid token — when CSRF is enabled, the path is not exempt, and the request is either session-authenticated (session-token cookie / X-Session-Token header) or a /web route. This covers /web, /web/admin, and any session-authenticated /api route. Token-auth API clients that send no session cookie are not CSRF-vulnerable and are not checked; safe methods (GET/HEAD/OPTIONS) and :exempt-paths are skipped.

;; resources/conf/<env>/config.edn under :active
:boundary/http
{:security
 {:csrf {:enabled?     true                                  ; OPT-IN: lib default is false
         :secret       #or [#env CSRF_SECRET #env JWT_SECRET] ; defaults to JWT_SECRET
         :exempt-paths ["/api/v1/payments/webhook"]}}}        ; trailing /* = segment-prefix

Enforcement is opt-in: the library default is :enabled? false, so upgrading the framework cannot start rejecting requests from a consumer that does not yet emit tokens. An app turns it on with the block above (after emitting tokens in its /web forms); the secret falls back to JWT_SECRET. Startup fails loud — the system wiring throws and the app refuses to boot if CSRF is enabled with a blank secret, rather than letting the interceptor fail open (run unvalidated).

Tokens are emitted with no per-handler wiring. For HTMX, either merge (csrf/hx-headers) onto an element’s attributes (e.g. <body>) so all inherited hx-* requests carry the X-CSRF-Token header, or rely on the shared page layout’s <meta name="csrf-token"> tag plus the global htmx:configRequest listener that attaches the header to every HTMX request. Plain (non-HTMX) forms include a hidden field:

(require '[boundary.platform.core.csrf :as csrf])

[:form {:method "post" :action "/web/logout"}
 (csrf/hidden-field)        ; reads the token bound for the current request
 [:button "Logout"]]

See the authentication guide for the request flow and the unauthenticated (login) pre-session token.

Testing

clojure -M:test:db/h2 :platform

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close