Changelog

All notable changes to ShipClojure-Datom will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

Added

  • RSS feed at /blog/feed.xml: Blog posts are now published as a valid RSS 2.0 feed, enabling submission to feed aggregators like Planet Clojure
    • Full RSS 2.0 with atom:link self-reference for feed validator compliance
    • Each item includes title, link, guid, description, publication date, author, and category tags
    • Feed is excluded from the sitemap
    • RSS auto-discovery <link> tag added to the HTML <head> for browser and feed reader detection
  • Multi-instance development: Run multiple dev instances in parallel via git worktrees with unique port blocks
    • bb prepare-instance [seed] — computes a contiguous block of 9 ports from a numeric seed and writes overrides to deps.local.edn
    • bb list-instances — shows all git worktrees with their configured ports
    • --docker-override flag generates docker-compose.override.yml for fully isolated Docker containers (unique names, volumes, network)
    • Seed can be a number, ticket ID (SC-1234), branch name, or auto-detected from current git branch
    • Conflict detection: scans worktree configs + TCP-probes before assigning ports
    • See docs/multi-instance.md for full documentation
  • Automatic port conflict detection: bb dev now checks if default ports are already in use (e.g. by another project) and automatically finds free alternatives at startup — no configuration required
  • Dev configuration summary: bb dev prints a clear startup banner showing services, browsable URLs, and all port assignments
  • :dev/options in deps.edn: project-specific dev configuration (:docker-compose, port overrides) merged from deps.edn (defaults) and deps.local.edn (local overrides, takes priority)
  • Docker Compose auto-detection: :docker-compose option in :dev/options with three modes — :auto (default, probes PostgreSQL + Transactor ports), true (always start), false (never start)
  • Service identity verification: Zero-dependency protocol-aware probes for PostgreSQL (SSLRequest wire protocol) and Datomic transactor (HTTP /health endpoint) — detects when ports are occupied by wrong services. Post-startup verification confirms services started correctly with retries and clear error messages.
  • Datomic transactor health port: Container health endpoint (internal 9999) mapped to host port 4335 via DEV_TRANSACTOR_HEALTH_PORT for protocol-level health checking

Changed

  • bin/launchpad refactored: reads :dev/options from both deps.edn and deps.local.edn (using launchpad/maybe-read-edn), injects DEV_* env vars for child processes, resolves ports and docker-compose status before printing a configuration summary
  • shadow-cljs.edn: ports now configurable via #shadow/env reader tags (DEV_SHADOW_NREPL_PORT, DEV_SHADOW_HTTP_APP_PORT, DEV_SHADOW_HTTP_PORTFOLIO_PORT)
  • docker-compose.yml: service ports now use ${DEV_*:-default} env var substitution for PostgreSQL, Memcached, and Datomic Transactor

Security

  • npm audit fixes: Resolved 4 audit vulnerabilities by upgrading direct dependencies
    • katex 0.12.00.16.28 — fixes URL protocol bypass, unescaped filenames, unvalidated attributes, maxExpand bypass (4 moderate)
    • markdown-it 14.1.014.1.1 — fixes ReDoS vulnerability (moderate)
    • @isaacs/brace-expansion — transitive fix via npm audit fix (high)
    • tar — transitive fix via @tailwindcss/cli upgrade (high)
    • Remaining 5 high-severity warnings are unfixable transitive deps from @chrisoakman/standard-clojure-style (dev-only)

Changed

  • Dependency Updates (npm):
    • apexcharts 5.3.45.6.0
    • punycode 2.1.12.3.1
    • @tailwindcss/cli 4.1.144.2.0
    • @tailwindcss/postcss 4.0.124.2.0
    • tailwindcss 4.1.144.2.0
    • daisyui 5.1.295.5.19
    • prettier 3.2.53.8.1
    • prettier-plugin-tailwindcss 0.6.110.6.14
    • shadow-cljs 3.1.83.3.6
    • snabbdom 3.5.13.6.3

Fixed

  • Fixed custom field schema for payment checkout
  • Theme system: Fixed "System" theme not correctly respecting prefers-color-scheme
    • set-dom-theme-attribute! now removes data-theme for :system/nil (instead of setting data-theme="system") so the OS media query applies naturally; also correctly removes the dark class when switching away from dark themes
    • FOUC prevention inline scripts in saas.ui.layout.core and saas.ui.layouts now remove data-theme for system/absent theme instead of defaulting to "light"
    • content.js fixed: uses correct localStorage key ("theme" instead of "saas.theme/theme") and properly removes data-theme for system mode
    • daisyui.css: custom light theme marked default: true and custom dark theme marked prefersdark: true so system mode uses the app's own branded themes rather than daisyUI's built-in defaults
    • App state now hydrated from localStorage on boot (saas.core/init! and saas.landing/init!) so the theme dropdown reflects the saved preference immediately on load
    • Extracted duplicated FOUC prevention script into saas.ui.layout.core/theme-init-script def, shared across both layout namespaces
    • Make cljc/saas/ui/components/render-theme-dropdown fully support server rendering through CSS selective rendering of the correct theme

[1.0.0] - 2026-02-08

Security

  • Token Tampering Detection: Enhanced authentication validation to detect and prevent token tampering
    • Implemented invalidate-token-family! function that clears all refresh tokens for a user when tampering is detected
    • Token signature mismatch now triggers immediate family invalidation and logs security warning
    • Both commands and queries now validate refresh token pairing when present in session
    • Updated authentication documentation to explain token tampering detection and family invalidation

Changed

  • Environment Lifecycle: Consistent use of env/defaults :init, :start, and :stop functions
    • saas.core/-main now calls :init before startup for proper logging configuration
    • saas.dev runs :init on namespace load and calls :start/:stop in reset/halt functions
    • user.clj now reuses integrant functions from saas.dev instead of duplicating them
    • Moved virgil/watch-and-recompile and *warn-on-reflection* setup into dev env :init
  • Logging Upgrade: Upgraded Telemere from 1.0.0-RC5 to 1.2.0
    • Added telemere-slf4j as the SLF4J backend, routing all SLF4J logs through Telemere
    • Removed logback-classic dependency in favor of unified Telemere logging
    • Excluded slf4j-nop from Powerpack to resolve SLF4J provider conflicts
    • Migrated all logback.xml configurations to Telemere set-min-level! calls in env.clj files
    • Added configure-dev-log-levels!, configure-prod-log-levels!, and configure-test-log-levels! functions
    • Removed all logback.xml files (dev, prod, test) - logging now fully managed by Telemere
  • Changed /auth/log-in handler schema to be aligned with rest of the handlers. :password, :email -> :user/password, :user/email
  • Changed transit serialization between client and server to use json in production and json-verbose in development for easier debugging locally
  • Muuntaja instance is now created per-environment via create-instance function, injected through Integrant instead of a static def
  • Upgraded Datomic peer from 1.0.7364 to 1.0.7387
  • Upgraded replicant to v2025.12.1

Added

Organisation Support

Multi-tenant organisation system with role-based membership and optimised query performance.

  • New organisation schema with id, name, slug, and memberships attributes
  • New membership schema linking users to organisations with roles (owner, admin, member)
  • Automatic personal organisation creation on user registration (name: "My Organisation", slug: username)
  • Users become owner of their personal organisation
  • New saas.datomic.organisation namespace with implicit partition utilities
    • org/tempid - creates tempids in the org's partition for index locality
    • org/org-partition - returns the implicit partition for an org ID
  • New u/->slug function for URL-friendly string conversion
  • Member invitation system: invite-member, accept-invitation, reissue-invitation commands with invitation schema attributes (:org.invitation/token, :org.invitation/email, :org.invitation/role, etc.)
  • Member management: remove-member and update-member-role commands for managing organisation members
  • get-organisation query: returns organisation details with members and their roles
  • register-member command: allows invited users to register without email verification
  • Organisation invitation email template: new email template for inviting users to join an organisation
  • Organisation settings page: full UI for managing org details, inviting members, and updating roles
  • Accept invite page: UI for accepting organisation invitations via token link
  • :query/get-account returns :user-account/role: the user's membership role within their organisation is now included in the account response
  • [DOCS] Added documentation for organisations: docs/backend/organisations.md

Breaking Changes:

  • Removed db.part/account partition - use implicit partitions via org/tempid instead

Route-Level Role-Based Access Control

Defence-in-depth authorisation enforced on both frontend and backend.

  • Declarative :auth/roles metadata on routes restricts access by membership role
  • routes/required-roles helper to look up role requirements for any route
  • Forbidden page: access denied screen shown when a user lacks the required role
  • CQRS role enforcement: commands/queries support :command/required-roles / :query/required-roles — middleware checks the user's role and returns 403 if insufficient
  • Sidebar role filtering: dashboard sidebar items are automatically hidden when the user's role doesn't match the route's :auth/roles
  • New subs/user-role subscription reads the user's membership role from DataScript

Other Additions

  • Support for ruby & kamal through mise for consistent version
  • docker-compose.yml for easy local development. See Getting Started for more details. It contains:
    • PostgreSQL storage for Datomic
    • Datomic transactor service
    • Datomic console
    • Memcache for caching consistent with production setup
  • Better logging context. Each request adds in the logging context the following: request-method, uri, request params - with obfuscation of sensitive data, user claims info.
  • launchpad support for streamlined development experience across all editors!
  • [DOCS] Better getting started documentation for new users.
  • [DOCS] Better docs for Repl Workflow and local customization options
  • [DOCS] Added docs on logging and log configuration
  • stripe-cli to be installed through mise
  • Reitit request coercion support for route definitions
  • Nexus :event/target.checked placeholder to get true|false value from a checkbox dom event
  • Tests for onboarding page
  • Show correct initials in avatar when user is registering through normal onboarding
  • Added support for data-type "uuid" for forms. This will automatically send the input's content as uuid
  • select-field and select form components: new form components in saas.forms for dropdown selects with automatic keyword value serialization
  • prepare-form-input-args helper in saas.forms: auto-clears field validation errors on user input for better UX
  • Test-only session handler: test-set-session handler to simulate session-setting flows (OAuth2, etc.) in integration tests
  • [DOCS] Added UI pages documentation: docs/frontend/ui-pages.md — guide for creating pages with the standard page pattern

Removed

  • Removed emacs specific configuration. It's now handled by launchpad
  • ShadowCLJS & CSS Watch integrant component logic - It's now handled by launchpad
  • :nrepl alias from deps.edn - replaced by usage of launchpad

Fixed

  • Fixed 404 page requests not returning correct not found screen
  • Fixed crash on :actions/clear-refresh-token when user was not found
  • Show "Username taken" server error on /complete-register route
  • Fixed bug where command/refresh-token would go in a loop when accessing authenticated routes without a session
  • Fixed arrow icon not expanding in dashboard sidebar when element was selected
  • Fixed queries & commands not merging under the same log if different token or options were used

[0.1.0]

Added

  • CRM Dashboard Page: New Customer Relationship Management dashboard with sample data and charts
  • DaisyUI Progress Component: Added progress bar component with comprehensive Portfolio scenes
  • Structured JSON Logging: Production-ready structured logging with request ID tracking
    • Automatic daily rotation with gzip compression
    • 30-day retention policy
    • One JSON object per line for easy parsing with jq
    • Request tracing across all operations
  • Kamal Log Utilities: Added Kamal aliases for easy log parsing and error tracking
    • kamal json-logs-errors - View all error logs
    • kamal json-logs-latest - View latest 100 logs
    • kamal json-logs-today - View today's logs
  • Sitemap Generation: Automatic sitemap.xml generation for static pages and blog posts
  • Enhanced Security: Query response filtering to exclude sensitive data
    • Password hashes no longer returned in :query/get-account
    • Refresh tokens excluded from account queries
  • Test Coverage: Added comprehensive tests for :query/get-account endpoint

Changed

  • Icon System Migration: Complete migration from Lucide icons to Phosphor icons
    • Removed 108+ individual SVG files (resources/icons/*)
    • Eliminated custom icon build system (src/bb/build_icons.clj)
    • Reduced bundle size and improved performance
    • Simplified icon usage via no.cjohansen/phosphor-clj library
    • Updated all UI components, Portfolio scenes, and tests
  • Dependency Updates:
    • Upgraded Clojure from 1.12.1 to 1.12.3
    • Updated standard-clojure formatter to 0.25.0
    • Updated Virgil from 0.4.0 to 0.5.0
    • Current stack: Replicant 2025.06.21, Nexus 2025.07.1, Powerpack 2025.06.16
  • Docker Configuration:
    • Added jq to runtime for JSON log parsing
    • Optimized persistent data locations
    • Fixed production build process
  • Static Asset Handling: Enhanced Powerpack middleware to serve generated resources (sitemap.xml)
  • Page Routing: Separated static pages from SPA routes for better SEO and performance

Fixed

  • Frontend Build: Resolved build issues related to icon system migration
  • Unit Tests: Fixed all tests affected by new icon system
  • Sidebar Tests: Updated component tests for Phosphor icon integration
  • Kamal Logging: Fixed log alias commands for production monitoring
  • Dockerfile: Resolved build issues with content copying and optimization
  • Integrant: Removed deprecated integrant calls in production logging setup
  • Settings Pages: Fixed styling and icon display issues

Removed

  • Lucide Icons: Completely removed legacy Lucide icon system
    • Deleted 108+ SVG icon files
    • Removed src/bb/build_icons.clj icon build script
    • Removed src/cljc/saas/ui/icons.cljc icon namespace
    • Cleaned up icon-related build tasks from bb.edn
  • Legacy Code: Removed code supporting old icon system

Documentation

  • Updated icon usage guide in docs/frontend/icons.md
  • Updated PROJECT_SUMMARY.md with latest architecture details
  • Enhanced deployment documentation with structured logging information

Performance

  • Reduced Package Dependencies: Significant reduction in npm package-lock.json size (2872 fewer lines)
  • Smaller Bundle Size: Icon migration reduced frontend asset size
  • Optimized Asset Delivery: Improved static content serving for production

Security Enhancement

If your code relies on receiving password hashes or refresh tokens from :query/get-account, note that these are now filtered out for security. Use dedicated endpoints for authentication operations.


Version History

This is the initial CHANGELOG for ShipClojure-Datom. Previous changes were tracked via git commits.

For detailed commit history, run:

git log --oneline --decorate