basecamp/once-campfire
48.6
Weak · 26 September 2026
4.1k
lines of production code
Ruby
with JavaScript
5
measurements over time
What this system is
Campfire is a self-hostable, real-time messaging application built on Rails 8.2 and Ruby 3.4 that facilitates private group communication through rooms, direct messages, and bot integrations. The system provides robust administrative controls, including IP-based user banning, granular room creation permissions, and secure session management, while ensuring data integrity through strict access scoping and content sanitization. It features a modernized rich-text editing experience with draft persistence and link previews, supported by a secure infrastructure that mitigates SSRF risks and supports Web Push notifications.
How it got here
2025 — Rails 8 upgrade and security hardening
44 changes.
The project upgraded to Rails 8.2 and Ruby 3.4, replacing the Trix rich-text editor with Lexxy and introducing comprehensive security measures such as IP-based user banning, SSRF protection for push subscriptions, and stricter access controls. These changes were accompanied by significant infrastructure improvements, including streamlined self-hosting via Docker, enhanced session management, and expanded test coverage for the new security and feature implementations.
2026 — Bot API and self-hosting tooling
8 changes.
This period focused on expanding bot capabilities by introducing an API for managing message boosts and providing corresponding JSON serialization views. It also enhanced operational support for self-hosted deployments through scripts for secret generation and automated SQLite database backup and restore hooks.
Features
Added JSON view templates for bot messages
New Jbuilder templates have been added for the 'by\_bots' message scope, enabling JSON serialization for both the index and show actions. The index template renders a collection of messages using the shared 'messages/message' partial, while the show template renders a single message using the same partial, allowing API consumers to receive structured JSON responses for messages sent by bots.
_app/views/messages/by\bots · high confidence
Added backup and restore hooks for SQLite database management
New shell scripts have been added to the hooks directory to automate database backup and restore operations. The pre-backup hook executes a preparation script, while the post-restore hook automatically copies a SQLite snapshot from the storage backups directory to the active database location, ensuring the database is restored from the latest snapshot if one exists.
hooks · high confidence
Bot API for managing message boosts
A new controller endpoint has been added to allow bots to create and delete their own boosts (reactions) on messages. This feature enables bots to interact with message content by adding or removing boosts, with specific validation to ensure content is present during creation.
app/controllers/messages/boosts · high confidence
New script to generate secrets for self-hosting
A new executable script, script/admin/generate-secrets, has been added to help users set up self-hosted instances. When run, it generates and outputs the necessary environment variables: SECRET\_KEY\_BASE, VAPID\_PRIVATE\_KEY, and VAPID\_PUBLIC\_KEY, using Ruby's SecureRandom and the web\_push library.
script · high confidence
Redesigned setup script and added release automation
The local development setup experience has been significantly improved: the bin/setup script now uses a modern, interactive UI powered by 'gum' to guide users through installing system dependencies (like mise, sqlite, ffmpeg), Ruby gems, and Redis, while also installing GitHub Actions linting tools (actionlint, shellcheck, zizmor). Additionally, new bin scripts have been introduced to streamline operations: bin/release automates the creation of GitHub releases, builds multi-platform Docker images, and exports source code ZIPs for deployment; bin/ci integrates ActiveSupport's Continuous Integration; bin/bundler-audit and bin/thrust provide direct access to security auditing and the Thruster proxy; and bin/brakeman and bin/rubocop now run with explicit flags to ensure latest versions and use project-specific configurations.
bin · high confidence
Removals
Removed Trix rich text editor vendor file
The vendor JavaScript file for the Trix rich text editor (version 2.0.10) has been removed from the project.
vendor · high confidence
Security
Guard push-subscription endpoints against SSRF
Push notification subscriptions are now validated to prevent Server-Side Request Forgery (SSRF) attacks. The system restricts endpoints to a specific list of permitted push service hosts (such as Google, Mozilla, Apple, and Windows) and enforces HTTPS on port 443. Additionally, DNS resolution is deferred and checked against private network ranges before delivery, ensuring that maliciously crafted subscription URLs cannot be used to access internal resources. This change also updates the WebPush notification delivery to accept an IP resolver and adjusts the delivery pool configuration.
_app/models/push, lib/web\push · high confidence
Security hardening and configuration updates in initializers
This update introduces several security and configuration improvements across the application's initializers. Active Storage direct upload endpoints are now protected by requiring a valid user session, preventing unauthorized blob allocation or disk writes. Web push subscription delivery is secured against SSRF by pinning HTTP connections to the resolved public IP address and disabling proxy discovery. Image processing is hardened by disabling unfuzzed libvips operations and blocking specific dangerous file types (BMP, ICO, Photoshop). Additionally, sensitive parameters like email, CVV, and CVC are now filtered from logs, the 'HTTP' acronym is enabled for inflection, and the application version now falls back to the Git revision if the APP\_VERSION environment variable is missing.
config/initializers · high confidence
Behavioural changes
Admin-only room creation toggle and separated user list
The account settings page now includes a toggle switch that allows administrators to restrict new room creation to administrators only. Additionally, the account members list has been reorganized to display administrators separately from regular members, with a visual divider between the two groups.
app/views/accounts · high confidence
Adopt surfguard gem for private IP address validation
The private network guard in lib/restricted\_http now delegates IP resolution and blocking logic to the surfguard gem instead of using local IPAddr checks and the Resolv library. This ensures that internal, loopback, and link-local addresses are classified consistently across Basecamp, HEY, and Fizzy, while resolving issues with IPv6-to-IPv4 mapping and NAT64 ranges that were previously bypassed or incorrectly handled by the custom implementation.
_lib/restricted\http · high confidence
Authorize room message streams and scope unread notifications per user
Room message subscriptions are now authorized at the time of subscription, ensuring that users only receive messages from rooms they currently belong to and that revoking membership immediately stops delivery. Additionally, the unread rooms notification stream is scoped to individual users, preventing users from seeing activity in rooms they are not part of.
app/channels · high confidence
Campfire self-hosting improvements and Ruby upgrade
The application runtime has been upgraded to Ruby 3.4.10, with the Dockerfile updated to include necessary build dependencies (libssl-dev) and runtime libraries (jemalloc, ffmpeg, etc.). Self-hosting is now streamlined with a new Dockerfile-export for local archiving, explicit ONCE backup/restore hooks, and comprehensive documentation including a CONTRIBUTING.md guide, a SECURITY.md trust model, and a detailed README covering ONCE and Docker deployment. Additionally, the Docker image now runs as a non-root user for security, includes OpenContainer annotations, and excludes .claude and README from the build context.
(repo-wide) · high confidence
Database schema updates for Active Storage, user bans, and account settings
This update applies several database migrations to support new features and framework requirements. It introduces a new 'bans' table to track IP-based user bans, migrates the 'users' table from an 'active' boolean to a 'status' integer field, and adds a 'settings' JSON column to the 'accounts' table to allow for configuration options like restricting room creation. Additionally, it includes standard Active Storage migrations to add service names, support image variants, and relax checksum constraints, ensuring compatibility with the upgraded Rails version.
db/migrate · high confidence
Enhanced security controls and refined user management in account administration
This update introduces stricter access controls and improves the reliability of account management. Administrators can now restrict room creation to administrators only via account settings, and a new background job handles the removal of banned content. The account editing interface now visually separates administrators from regular members and allows admins to view banned users, while the session management has been updated to properly terminate server-side sessions on logout. Additionally, the system now includes IP-based banning capabilities and handles race conditions during initial account creation more gracefully.
app/controllers · high confidence
Enhanced session management and access control in controller concerns
This change introduces new controller concerns and refines existing authentication logic to improve security and session handling. A new \ActiveStorageAuthentication\ concern now enforces authentication for direct uploads, while \BlockBannedRequests\ adds IP-based banning that returns a 429 Too Many Requests error for banned IPs on non-safe requests. The existing \Authentication\ concern has been updated to support session termination on sign-out, which now explicitly destroys the current session, resets the session state, removes the authentication cookie, and disconnects remote connections (such as Action Cable) for the user. Additionally, a \RawRequestBody\ concern was added to provide a helper for reading the raw request body with proper encoding and rewinding.
app/controllers/concerns · high confidence
Expanded bot message management and stricter access controls
Bots can now read, update, and delete their own messages, with the new \Messages::ByBotsController\ exposing index, create, update, and destroy endpoints that include pagination headers and room membership verification. Additionally, the ability for bots to remove their own boosts has been enabled by updating the \Messages::BoostsController\ to allow destruction of boosts where the bot is the booster, while ensuring boosts remain scoped to the current message.
app/controllers/messages · high confidence
Hardened link previews and secured bot key logging
Link previews are now restricted to external web URLs, preventing local host spoofing and ensuring previews only display content from other domains. The system also supports both legacy Trix and new Lexxy editor formats for these previews. Additionally, bot keys in request logs are now redacted to prevent accidental exposure of sensitive credentials.
_lib/rails\ext · high confidence
Hardened message content filtering with dedicated attribute sanitization
The content filtering system now includes a dedicated filter to scrub unsafe attributes (such as event handlers and dangerous URI schemes) from allowed HTML tags, ensuring that presentation styling classes are preserved while maintaining security. This change also refactors the handling of solo unfurled link text to support both Trix and Lexxy editors, and expands the set of allowed HTML tags to include additional formatting elements.
_app/helpers/content\filters · high confidence
IP-based user banning and enhanced session management
Administrators can now ban users by IP address using the new Ban model, which validates that the provided IP is public. User accounts now support a 'banned' status alongside active and deactivated states, and the User model includes a dependency to destroy associated bans. Session handling has been refined: the Current model now tracks the session object to automatically sync the current user, and logging out explicitly destroys the server-side session. Additionally, account settings can now restrict room creation to administrators only, and room types are protected so direct rooms cannot be converted to open or closed rooms.
app/models · high confidence
Improved robustness and styling for Open Graph embeds
The Open Graph embed component now handles missing URLs more safely by only rendering the link when a href is present, preventing broken links. It also uses the description field instead of the caption for the embed text and applies a specific CSS class when a Twitter avatar is detected, ensuring better visual consistency and stability for users viewing rich link previews.
_app/views/action\text · high confidence
Message presentation updates and editor switch
The message editing interface now uses the Lexxy rich-text editor instead of Trix, with corresponding changes to the edit form's data attributes and CSS classes. To support right-to-left languages, the message presentation container now includes dir="auto". Additionally, the message view's fragment cache key has been bumped to version 3 to bust stale caches when presentation filters change, and new JSON builders have been added to serialize message and boost data for API responses.
app/views/messages · high confidence
Migrate from Trix to Lexxy editor and enable Action Cable under custom script names
The application's rich text editing capability has switched from the Trix editor to Lexxy, evidenced by the new \lexxy-content\ CSS class in the Action Text layout and the introduction of \lexxy-prompt-item\ components for user autocompletion. Additionally, the main application layout now includes a meta tag to support serving Action Cable traffic under a custom \$SCRIPT\_NAME\, allowing WebSocket connections to function correctly when the app is mounted at a sub-path.
app/views/layouts · high confidence
Migrate rich text editor from Trix to Lexxy and harden link previews
The application replaces the Trix rich text editor with Lexxy, updating the composer, autocomplete, and message formatting logic to use the new engine. This migration includes removing legacy Trix-specific controllers and libraries, adding a new \unfurl\_controller\ to handle Open Graph link previews with proper HTML escaping, and introducing draft persistence in the composer to retain unsent messages when switching rooms. Additionally, service worker registration is fixed to use \window.location.origin\ instead of the host, and code block highlighting is improved to handle line breaks correctly.
app/javascript · high confidence
Optimized Open Graph fetching and Resque connection handling
The Open Graph fetching logic now uses StringIO instead of a String for accumulating response bodies, which improves memory efficiency and allows for cleaner retrieval of the final string. Additionally, the Resque pool setup task now explicitly closes the Redis client connection after forking, replacing the previous reconnect call to ensure proper resource management in the worker processes.
app/models/opengraph, lib/tasks · high confidence
Rails 8 configuration updates and enhanced logging
The application environment configurations have been updated to align with Rails 8 and Ruby 3.4.5 standards. In development, code reloading is now managed via \config.enable\_reloading\ instead of the deprecated \config.cache\_classes\, and new logging features like query log tags, verbose enqueue logs, and view filename annotations are enabled. Production settings now use a custom \LogScrubbingFormatter\ to redact sensitive data from logs, implement granular cache-control headers for assets versus other files, and suppress deprecation reports. Test environment configurations have also been updated to use \enable\_reloading\ and raise errors for missing callback actions, ensuring stricter validation during testing.
config/environments · high confidence
Rails 8.2 upgrade, editor swap, and new bot/ban capabilities
The application has been upgraded to Rails 8.2 (config.load\_defaults 8.2), which includes switching the schema format from SQL to the default Ruby schema (schema.rb) and adjusting autoload settings. The rich text editor has been replaced from Trix to Lexxy, reflected in the importmap configuration. New functionality allows bots to read, create, update, and destroy their own messages and boosts via a new JSON API endpoint under /rooms/:room\_id/:bot\_key, and administrators can now ban users via a new /users/:user\_id/ban route. Configuration has also been hardened to support Redis connection via the REDIS\_URL environment variable, and database retry logic has been replaced with a timeout setting.
config · high confidence
Refined room access control and scoping for specific room types
The room controllers now enforce stricter access rules and scope room lookups based on the specific room type being managed. The \ClosedsController\ and \OpensController\ restrict room creation to users with explicit permissions and scope room lookups to exclude direct rooms, preventing accidental conversion of direct rooms into open or closed types. The \DirectsController\ now sets the room instance variable for edit and destroy actions and scopes room lookups to only direct rooms, ensuring that users can only administer direct rooms they are part of. These changes enhance security by preventing unauthorized room type conversions and ensuring that users can only interact with the appropriate room types.
app/controllers/rooms · high confidence
Refined user management controls and banned state styling
The user list component now visually indicates when a user is banned by applying a 'banned' CSS class. Admin controls for role assignment are now disabled for the current user to prevent self-modification, and the delete button is explicitly hidden for the current user to prevent self-deletion. Additionally, the logic for displaying the 'My settings' link has been adjusted to ensure it appears for the current user regardless of admin privileges, while admin actions remain gated by active status.
app/views/accounts/users · high confidence
Replace Trix rich-text editor with Lexxy
The application's rich-text editing engine has been switched from Trix to Lexxy. This change updates the underlying editor components and their associated CSS, ensuring that the new Lexxy editor integrates seamlessly with the existing Campfire design system. Styles have been adjusted to map Lexxy's internal color palette to the app's light/dark themes, maintain consistent toolbar behavior, and preserve the look and feel of message bodies, embeds, and mention menus. A new cancel icon asset was added to support the updated UI elements.
app/assets · high confidence
Restrict new room creation to administrators
The 'New Chat Room' button in the user sidebar is now conditionally hidden based on account settings. If the account setting 'restrict\_room\_creation\_to\_administrators' is enabled, only users with administrator privileges can see and click the button to create new rooms; regular users will no longer see this option.
app/views/users/sidebars · high confidence
Rich text editor replaced with Lexxy and Japanese language support added
The rich text editor has been replaced from Trix to Lexxy, requiring updates to the message sanitization logic to allow formatting tags (such as tables, strikethrough, and underlines) that were previously stripped. To ensure existing messages render correctly with the new editor, attachment content is now rebuilt from its source before editing. Additionally, Japanese translations have been added to the application's UI strings, and a helper was introduced to correctly configure ActionCable URLs when the application is served under a script name prefix.
app/helpers · high confidence
Support Lexxy rich text editor mentions via new HTML response format
The users autocomplete controller now supports the Lexxy rich text editor's mention feature by rendering an HTML response without a layout for the \html\ format, specifically to return \\<lexxy-prompt-item\>\ elements. Additionally, the controller logic has been updated to accept a \filter\ parameter (in addition to the existing \query\ parameter) to accommodate Lexxy's filtering mechanism, ensuring that mentions are correctly filtered when the user types in the rich text editor.
app/controllers/autocompletable · high confidence
Switch database schema format from structure.sql to schema.rb
The database schema representation has changed from the database-specific \structure.sql\ to the database-agnostic \schema.rb\. This means the application now relies on Rails migrations to define the database structure, which improves portability across different database engines (such as switching from SQLite to PostgreSQL) and simplifies version control by storing schema changes in a Ruby format rather than raw SQL. The \schema.rb\ file now serves as the source of truth for the current database state, including tables for accounts, users, messages, bans, and other core entities.
db · high confidence
Unread room notifications are now scoped per user
The system has changed how it notifies users about unread rooms. Previously, a single global broadcast was sent whenever a message was created, potentially notifying all users. Now, the notification is fanned out specifically to the members of the room where the message was posted, ensuring that only relevant users receive the update and reducing unnecessary noise for users not involved in that specific conversation.
app/models/message · high confidence
Upgrade message composer to Lexxy with draft persistence and link unfurling
The room message composer has been replaced with Lexxy, introducing automatic draft saving when switching rooms and support for link unfurling. The composer now handles specific attachment types (mentions and opengraph-embeds) and utilizes a new 'unfurl' controller to process inserted links, enhancing the user experience when composing messages in real-time.
app/views/rooms · high confidence
User ban management and profile view updates
Administrators can now ban and unban users directly from the user profile page via a new ban button that triggers a confirmation dialog warning about logout, message deletion, and IP blocking. The profile view visually indicates when a user is banned, and the ban/unban action is conditionally shown only to administrators who are not viewing their own profile. Additionally, the user profile JSON serialization now includes an avatar URL, and inline mentions in text content are rendered as spans instead of divs to prevent paragraph breaking.
app/views/users · high confidence
User banning, avatar variants, and bot authentication fixes
This update introduces IP-based user banning, allowing administrators to ban users by IP address, which automatically closes remote connections, deletes active sessions, and schedules the removal of banned content. User avatars now support a square WebP variant (512x512) for optimized display. A security fix ensures bot authentication correctly filters for active bots only, preventing potential impersonation via inactive bot tokens. Additionally, user mentions now use a custom content type for better attachment handling, and the user role enum is explicitly typed.
app/models/user · high confidence
User banning, push subscription validation, and avatar/logo refactoring
Administrators can now ban and unban users via the new bans controller. Push subscription creation now validates the subscription data before accepting it, returning an error for invalid requests instead of blindly creating or touching records. Avatar and logo serving logic has been refactored to use dynamic variant methods instead of hardcoded resize constants, and the sidebar placeholder query now prevents negative limits when a user has many direct conversations.
app/controllers/users · high confidence
Test coverage
Added controller tests for authentication, security, and link previews; Added integration tests for the bot messages API; Added system tests for the new Lexxy composer and link preview security; Added test helpers for DNS resolution stubbing and Lexxy rich text editor interactions; Added tests for Action Text attachables, OpenGraph embeds, and log scrubbing; Added tests for Web Push SSRF mitigation and updated test infrastructure; Added tests for bot message boost API; Added tests for message sanitization and rich-text helper behavior; Added tests for private network guard SSRF protections; Added tests for room creation restrictions and type conversion safeguards; Added tests for room message and unread rooms channel authorization; Added tests for user banning, avatar/logo fallbacks, and push subscription SSRF protection; Expanded test coverage for security, model logic, and image processing.
Dependencies
Upgrade to Rails 8.2.0 alpha and update core dependencies
The application has been upgraded from Rails 7.2.0 alpha to Rails 8.2.0 alpha, requiring updates to core gems such as Puma (6.4 to 7.2), Redis (4.0 to 5.4), and Resque (2.6 to 2.7). The rich text editor has been switched from Trix to Lexxy, and the \surfguard\ gem has been added to handle SSRF address policies. Additionally, \bundler-audit\ was added to the development group for security auditing, and standard library gems like \ostruct\ and \benchmark\ are now explicitly declared.
(dependencies) · high confidence
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
How this codebase got here
This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings — which rule fired, in which file, on which line, and how to fix it — are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.
Score
- CAI 43 → 49 (+5.7)
- Rubric changed (rubric-2026.08.15 → rubric-2026.09.15) — scores are not directly comparable.
Lenses
- Code Health 53 → 70 (+17.9)
- Architecture 97 → 82 (-14.4)
- Maturity 59 → 59 (-0.3)
- Readiness 22 → 64 (+42.3)
- Security 78 → 85 (+7.4)
- Domain Modelling 50 (new)
- Accessibility 37 (new)
Resolved (11)
- Coverage not measured — test suite did not build
- Dimension evaluation failed
- Duplicated block (10 lines × 2) (db/migrate/20231215043540_create_initial_schema.rb)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- Medium: security finding (details withheld)
- No exposed public API
- No tests found
- Test reliability not included
New (24)
- Duplicated block (9 lines × 2) (db/migrate/20231215043540_create_initial_schema.rb)
- FixmeComment (app/models/rooms/direct.rb)
- High IaC: WD-DOCKER-0006 (Dockerfile-export)
- Medium IaC: WD-DOCKER-0003 (Dockerfile)
- Medium IaC: WD-DOCKER-0003 (Dockerfile-export)
- Medium: security finding (details withheld)
- Medium: security finding (details withheld)
- Medium: security finding (details withheld)
- Medium: security finding (details withheld)
- No ADRs found
- Off-boarding risk: anonymized user #1
- Outdated: debug
- Outdated: faker
- Outdated: image_processing
- Outdated: jbuilder
- Outdated: lexxy
- Outdated: net-http-persistent
- Outdated: selenium-webdriver
- Outdated: sentry-rails
- Outdated: sentry-ruby
- …and 4 more
Changes since last survey
- 70 commits — 70 feature/other, 0 fixes
By area
- (repo) — 16 commits
- (root) — 12 commits
- app/controllers — 6 commits
- app/javascript — 6 commits
- .github/workflows — 5 commits
- app/helpers — 5 commits
- lib/rails_ext — 5 commits
- app/views — 3 commits
- .github/dependabot.yml — 2 commits
- test/lib — 2 commits
- app/assets — 1 commit
- bin/release — 1 commit
- config/cable.yml — 1 commit
- config/environments — 1 commit
- lib/web_push — 1 commit
- test/controllers — 1 commit
- test/helpers — 1 commit
- test/system — 1 commit
Notable commits
- change: Adapt the Lexxy composer to main's link preview hardening
- change: Adjust to match in-house style
- change: Adjust to the in-house style
- change: Allow bots to delete their own boosts
- change: Allow bots to update and destroy their own messages
- change: Assert only that the preview image gained no extra attributes
- change: Bump actionlint and zizmor-action to current releases (#250)
- change: Bump brakeman to 8.0.6 and stop --ensure-latest reddening CI (#249)
- change: Bump json from 2.20.0 to 2.21.2
- change: Bump ruby/setup-ruby to v1.321.0
- change: Bump surfguard to the published 0.2.0 (#271)
- change: Bust the cached message presentation
- change: Check the registry login before releasing
- change: Close live Action Cable connections on sign out (#268)
- change: Compare a preview's host to ours with the escapes resolved
- change: Cover markup-only OpenGraph title and description in link previews
- change: Drop the unsupported semver cooldown keys from the docker ecosystem (#251)
- change: Ensure backwards compatibility with Trix
- change: Escape the OpenGraph image URL in link previews
- change: Fix Codex's code review comments
- …and 50 more
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
Survey your own repository
basecamp/once-campfire was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.
About this page
- The score is its most recent published measurement, taken on 26 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
- Measured at commit 90b330024dec3e757c79b6a7e6568f93da8e3148 — the exact code this score is about.
- Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
- Measured by watchdog.canine.dev using codehealth-analyzer preprod-7c1cb6328e11.