influitive/apartment
Database multi-tenancy for Rack (and Rails) applications
Stale and unlicensed — last commit 2y ago
worst of 4 axesno license — legally unclear; last commit was 2y ago
no license — can't legally use code
Documented and popular — useful reference codebase to read through.
no license — can't legally use code; last commit was 2y ago
- ✓17 active contributors
- ✓Distributed ownership (top contributor 49% of recent commits)
- ✓CI configured
Show 3 more →Show less
- ✓Tests present
- ⚠Stale — last commit 2y ago
- ⚠No license — legally unclear to depend on
What would change the summary?
- →Use as dependency Concerns → Mixed if: publish a permissive license (MIT, Apache-2.0, etc.)
- →Fork & modify Concerns → Mixed if: add a LICENSE file
- →Deploy as-is Concerns → Mixed if: add a LICENSE file
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Great to learn from" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/influitive/apartment)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/influitive/apartment on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: influitive/apartment
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/influitive/apartment shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
AVOID — Stale and unlicensed — last commit 2y ago
- 17 active contributors
- Distributed ownership (top contributor 49% of recent commits)
- CI configured
- Tests present
- ⚠ Stale — last commit 2y ago
- ⚠ No license — legally unclear to depend on
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live influitive/apartment
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/influitive/apartment.
What it runs against: a local clone of influitive/apartment — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in influitive/apartment | Confirms the artifact applies here, not a fork |
| 2 | Default branch development exists | Catches branch renames |
| 3 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 4 | Last commit ≤ 726 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of influitive/apartment. If you don't
# have one yet, run these first:
#
# git clone https://github.com/influitive/apartment.git
# cd apartment
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of influitive/apartment and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "influitive/apartment(\\.git)?\\b" \\
&& ok "origin remote is influitive/apartment" \\
|| miss "origin remote is not influitive/apartment (artifact may be from a fork)"
# 3. Default branch
git rev-parse --verify development >/dev/null 2>&1 \\
&& ok "default branch development exists" \\
|| miss "default branch development no longer exists"
# 4. Critical files exist
test -f "lib/apartment.rb" \\
&& ok "lib/apartment.rb" \\
|| miss "missing critical file: lib/apartment.rb"
test -f "lib/apartment/adapters/abstract_adapter.rb" \\
&& ok "lib/apartment/adapters/abstract_adapter.rb" \\
|| miss "missing critical file: lib/apartment/adapters/abstract_adapter.rb"
test -f "lib/apartment/tenant.rb" \\
&& ok "lib/apartment/tenant.rb" \\
|| miss "missing critical file: lib/apartment/tenant.rb"
test -f "lib/apartment/elevators/generic.rb" \\
&& ok "lib/apartment/elevators/generic.rb" \\
|| miss "missing critical file: lib/apartment/elevators/generic.rb"
test -f "lib/apartment/railtie.rb" \\
&& ok "lib/apartment/railtie.rb" \\
|| miss "missing critical file: lib/apartment/railtie.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 726 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~696d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/influitive/apartment"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Apartment is a Ruby gem providing database-level multi-tenancy for Rails and Rack applications using schema isolation or database switching. It allows a single Rails app to serve multiple isolated tenants by automatically routing database queries to tenant-specific schemas (PostgreSQL) or databases (MySQL, SQLite), with support for shared data in a 'public' schema. Core gem structure: lib/apartment/adapters/ contains database-specific implementations (PostgreSQL, MySQL, SQLite, JDBC variants), lib/apartment/elevators/ handles tenant resolution from requests (subdomain, domain, host-based routing), lib/apartment/tenant.rb manages tenant lifecycle, and lib/apartment/railtie.rb integrates with Rails. Generators (lib/generators/apartment/install/) scaffold the config/initializers/apartment.rb setup file. Rake tasks in lib/tasks/apartment.rake provide tenant management utilities.
👥Who it's for
Rails developers building SaaS platforms or multi-tenant applications who need to isolate customer data at the database level without running separate application instances. Product teams at companies like Influitive who manage platforms serving multiple organizations.
🌱Maturity & risk
Production-ready and actively maintained. The repo shows 151KB of Ruby code, comprehensive test coverage via RSpec (.rspec present), CI/CD via Travis CI (.travis.yml), and support for Rails 4.2 through 6.0+ (Appraisals gemfiles). However, the latest visible updates suggest development activity has slowed (check HISTORY.md for recent release dates).
Moderate risk: Apartment is deeply integrated into Rails request lifecycle via Railtie hooks (lib/apartment/railtie.rb), making upgrades between Rails versions potentially fragile. Dependencies on database adapter gems (postgresql, mysql2, sqlite3) add version compatibility complexity. Single-maintainer risk visible in the Influitive organization; check GitHub issues for response time and PR review cycles. Breaking changes in Rails or ActiveRecord could require significant updates.
Active areas of work
Unable to determine from provided data (no .git log, recent commits, or PR list visible). Check the repo's GitHub page for recent merged PRs, open issues, and HISTORY.md for the last release notes. The gemfiles directory suggests active testing against Rails 5.2 and 6.0, but development velocity is unclear.
🚀Get running
Clone: git clone https://github.com/influitive/apartment.git && cd apartment. Install: bundle install (uses Gemfile; see Appraisals for multi-Rails-version testing). Run specs: bundle exec rspec. To test with specific Rails version: bundle exec appraisal rails-6-0 rspec.
Daily commands:
Development: bundle exec rspec (RSpec configured in .rspec). Interactive testing: bundle exec pry (see .pryrc for setup). Specific adapter tests: read Rakefile for available tasks. Docker support available (docker-compose.yml present, suggests PostgreSQL/MySQL test databases can be spun up).
🗺️Map of the codebase
lib/apartment.rb— Main entry point and configuration hub; orchestrates all tenant switching and database isolation logiclib/apartment/adapters/abstract_adapter.rb— Core abstraction defining the tenant isolation API that all database-specific adapters must implementlib/apartment/tenant.rb— Central tenant context manager; handles switching between tenant schemas and state managementlib/apartment/elevators/generic.rb— Request routing middleware that determines which tenant to activate based on incoming request contextlib/apartment/railtie.rb— Rails integration layer; bootstraps Apartment within Rails initialization and manages lifecycle hookslib/apartment/migrator.rb— Handles multi-tenant database migrations, running them across all configured tenantslib/apartment/adapters/postgresql_adapter.rb— Primary production adapter implementing schema-based tenancy for PostgreSQL
🧩Components & responsibilities
- Elevator (Subdomain/Domain/HostHash) (Rack middleware, HTTP request introspection) — Extracts tenant identifier from HTTP request and determines active tenant before app processing
- Failure mode: Unable to determine tenant → defaults to public schema or raises error; misconfiguration causes cross-tenant data leakage
- Tenant Context Manager (Thread-local variables, Rails request context) — Maintains current tenant state in thread-local storage; coordinates tenant switching across request lifecycle
- Failure mode: Context pollution between concurrent requests; stale tenant on thread reuse in connection pools
- Database Adapter (SQL DDL/DML, database connection management) — Executes database-specific commands to switch isolation scope (PostgreSQL schema vs MySQL database)
- Failure mode: Schema switch failure silently executes against wrong tenant; adapter mismatch between gem and database version
- Migrator (ActiveRecord migration framework, schema versioning) — Orchestrates ActiveRecord migrations across all tenant schemas/databases sequentially
- Failure mode: Migration failure on single tenant leaves inconsistent state; roll
🛠️How to make changes
Add support for a new database system
- Create new adapter class inheriting from AbstractAdapter (
lib/apartment/adapters/abstract_adapter.rb) - Implement required methods: setup, drop, switch, create, etc. (
lib/apartment/adapters/<new_database>_adapter.rb) - Register adapter in Apartment.configure with appropriate condition (
lib/apartment.rb) - Add adapter tests mirroring existing pattern (
spec/adapters/<new_database>_adapter_spec.rb) - Update Appraisals for supported Rails versions (
Appraisals)
Add a new request routing strategy (Elevator)
- Create new elevator inheriting from Generic (
lib/apartment/elevators/generic.rb) - Implement tenant determination logic in call() method (
lib/apartment/elevators/<strategy_name>.rb) - Register in Rails middleware stack via Railtie (
lib/apartment/railtie.rb) - Document strategy and configuration in template (
lib/generators/apartment/install/templates/apartment.rb)
Extend tenant lifecycle with custom hooks
- Add callback methods to Apartment.configure block (
lib/apartment.rb) - Trigger callbacks in Tenant.switch method (
lib/apartment/tenant.rb) - Test callbacks in suite using dummy app (
spec/apartment_spec.rb)
Support a new Rails version
- Add gemfile for new version (
gemfiles/rails_X_Y.gemfile) - Add version to Appraisals matrix (
Appraisals) - Update CI configuration (
.travis.yml) - Verify all adapter specs pass (
spec/adapters/)
🔧Why these technologies
- PostgreSQL schema-based isolation — Provides logical namespace isolation with full query transparency; recommended for production multi-tenancy
- MySQL database-per-tenant switching — Works within MySQL limitations; useful when schema switching unavailable or for larger data isolation boundaries
- Rack middleware pattern (Elevators) — Framework-agnostic request routing layer; allows tenant determination before application execution
- ActiveRecord adapter pattern — Integrates seamlessly with Rails ORM; minimal application code changes required for multi-tenancy
⚖️Trade-offs already made
-
Schema-based (PostgreSQL) vs database-based (MySQL) isolation strategies
- Why: PostgreSQL offers schema-level security and query transparency; MySQL requires separate databases per tenant or connection switching
- Consequence: PostgreSQL recommended for new projects; MySQL support adds complexity but maintains flexibility
-
Automatic tenant switching via middleware vs explicit switching
- Why: Elevators provide transparent per-request isolation; explicit switching allows fine-grained control
- Consequence: Majority of requests auto-switched; rare operations can override context for shared data access
-
Single-process context storage vs connection pooling per tenant
- Why: Apartment uses thread-local context for simplicity; avoids complex connection management
- Consequence: Scales to moderate multi-tenancy; not optimized for very high tenant counts
🚫Non-goals (don't propose these)
- Does not handle multi-region database replication or geo-distribution
- Does not manage authentication or authorization (only data isolation)
- Not designed for row-level security; operates at schema/database boundary level
- Does not provide cross-tenant querying or data aggregation mechanisms
- Does not implement automatic tenant billing or usage metering
🪤Traps & gotchas
PostgreSQL requires prepared statements to work across schemas (Rails ~> 3.1.2 patch mentioned in README). Memory leaks possible in ActiveRecord 4.x with Apartment (documented in README; upgrading Rails or using the influitive/rails fork is the workaround). Tenant name is prefixed with environment (e.g., 'development_tenant_name') when using prepend_environment config — this affects migrations and seed data. Migrations and schema.rb must be run per-tenant or against the shared 'public' schema; understand which using config.excluded_models. JDBC adapters require JRuby; don't mix with MRI-only gems.
🏗️Architecture
💡Concepts to learn
- PostgreSQL Schemas — Apartment's primary isolation strategy for PostgreSQL uses schemas; understanding their scope and isolation guarantees is critical for correct tenant separation
- Adapter Pattern — Apartment uses adapters (AbstractAdapter, MySQLAdapter, PostgreSQLAdapter) to abstract database-specific implementation; essential for understanding how to add new database support
- Middleware / Rack Pipeline — Apartment integrates via Rack middleware to intercept requests and set tenant context automatically; understanding middleware ordering and request/response cycle is needed for debugging
- Connection Pooling — Apartment manages ActiveRecord connection pools per-tenant to avoid leaks; misunderstanding this can cause database connection exhaustion
- Database Schema Migrations — Running migrations across multiple tenant schemas or databases is non-trivial; Apartment provides migration helpers to avoid data corruption
- Tenant Resolution / Elevator Pattern — Elevators determine which tenant to activate based on request properties (subdomain, domain, host header); custom elevators are often needed for unique routing schemes
🔗Related repos
rails/rails— Core framework Apartment extends; understanding ActiveRecord connection management and Railtie hooks is essentialinfluitive/apartment-sidekiq— Companion gem handling background job tenant context in Sidekiq when using Apartmentcitusdata/citus— Alternative multi-tenancy approach using PostgreSQL distributed tables; shows contrast to schema-based isolationTwilioDevEd/twilio-ruby— Example SaaS product that commonly uses Apartment for tenant isolation in production deployments
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive elevator strategy tests for edge cases
The elevators directory (lib/apartment/elevators/) contains 5 different tenant resolution strategies (domain, first_subdomain, generic, host, host_hash, subdomain), but spec coverage appears minimal. Each elevator handles critical tenant routing logic and edge cases like missing subdomains, malformed hosts, or nil values need explicit test coverage to prevent tenant isolation bugs.
- [ ] Create spec/elevators/ directory with individual test files for each elevator strategy
- [ ] Add tests in spec/elevators/subdomain_spec.rb for: valid subdomains, missing subdomains, multi-level subdomains, special characters
- [ ] Add tests in spec/elevators/host_spec.rb for: valid hosts, IP addresses, ports, host_hash fallback behavior
- [ ] Add tests in spec/elevators/domain_spec.rb for: domain extraction, TLD handling, subdomain combinations
- [ ] Add integration tests verifying tenant switching across elevator strategies using the dummy Rails app
Add GitHub Actions CI workflows for multiple Ruby/Rails versions
The repo uses .travis.yml (legacy CI) and tests against Rails 4.2 through master via Appraisals gemfiles, but GitHub Actions workflows are missing. This prevents modern, faster PR checks and leverages the .github/ISSUE_TEMPLATE.md that's already in place.
- [ ] Create .github/workflows/test.yml with matrix jobs for Ruby 2.6+ and Rails 4.2, 5.0, 5.1, 5.2, 6.0, master
- [ ] Add conditional database setup (PostgreSQL, MySQL, SQLite3) matching spec/adapters/ test files
- [ ] Add separate workflow file .github/workflows/lint.yml for RSpec and code quality checks using existing .rspec config
- [ ] Test against JDBC adapters (JRuby) in a separate workflow step referencing spec/adapters/jdbc_*_adapter_spec.rb
- [ ] Add status badge to README.md pointing to GitHub Actions instead of Travis CI
Add adapter-specific integration tests for schema operations
The lib/apartment/adapters/ contains 7 database adapters (PostgreSQL, MySQL, SQLite3, PostGIS, JDBC variants) but spec/adapters/ tests appear to be missing or incomplete. Each adapter's create_schema, drop_schema, and switch behavior differs significantly, requiring adapter-specific test suites to prevent regression.
- [ ] Expand spec/adapters/postgresql_adapter_spec.rb with tests for: schema creation, DROP IF EXISTS behavior, search_path switching, extension handling
- [ ] Expand spec/adapters/mysql2_adapter_spec.rb with tests for: database creation/deletion, CHARACTER SET handling, user privilege grants
- [ ] Expand spec/adapters/sqlite3_adapter_spec.rb with tests for: file-based database attachment, detachment, concurrent access
- [ ] Add spec/adapters/postgis_adapter_spec.rb to verify PostGIS extension compatibility during schema operations
- [ ] Add parametrized tests using shared examples in spec/adapters/shared_adapter_examples.rb for common operations across all adapters
🌿Good first issues
- Add comprehensive docs for the elevator pattern: lib/apartment/elevators/generic.rb lacks inline examples. Create a tutorial file explaining how to implement a custom elevator for non-standard tenant resolution (e.g., API key-based or query parameter-based).
- Write integration tests for SQLite adapter (lib/apartment/adapters/sqlite3_adapter.rb) covering multi-database switching and concurrent tenant access; currently appears to be the least-tested adapter based on file structure.
- Modernize the Rakefile and spec setup to support Rails 7.0+: update Appraisals gemfiles to include rails_7_0.gemfile and rails_7_1.gemfile, then fix any deprecation warnings in the test suite.
⭐Top contributors
Click to expand
Top contributors
- @meganemura — 49 commits
- @mikecmpbll — 25 commits
- @shterrett — 5 commits
- [@Mike Campbell](https://github.com/Mike Campbell) — 5 commits
- @ancorcruz — 2 commits
📝Recent commits
Click to expand
Recent commits
f266f73— Merge pull request #603 from tachyons/feat_rails_6_test (mikecmpbll)2b76f9b— Enable Rails 6 and add it to test matrix (tachyons)4716ae5— Prepare 2.2.1 release (mikecmpbll)5585dac— Merge pull request #588 from PatientWisdom/feature/expose-environmentify (mikecmpbll)5fd9391— Merge pull request #566 from Pysis868/patch-1 (mikecmpbll)ca3b4f6— Merge pull request #599 from lcjury/feature/add-callbacks-section-to-readme (mikecmpbll)f92b459— Merge pull request #586 from artemave/development (mikecmpbll)dc50eb0— Adds callbacks usage to the readme (Jury)763c072— Move environmentify to a public class method (AJ Miller)927ba21— IgnoreCREATE SCHEMA publicstatement in pg dump (artemave)
🔒Security observations
- High · Empty Database Root Password in Docker Compose —
docker-compose.yml. PostgreSQL container is configured with an empty password (POSTGRES_PASSWORD: ""), and MySQL is configured with MYSQL_ALLOW_EMPTY_PASSWORD: "yes". This allows unauthenticated access to the database services, which is a critical security risk even in development environments as it can be accidentally exposed. Fix: Use strong, randomly generated passwords for database services. Example:POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"with secrets management, or use Docker secrets/environment variables from secure sources. Never commit credentials to version control. - High · Exposed Database Ports —
docker-compose.yml - postgresql and mysql services. PostgreSQL (port 5432) and MySQL (port 3306) are exposed to the host machine without network segmentation or authentication enforcement. Combined with empty passwords, this creates a significant attack surface. Fix: Limit port exposure to localhost only (127.0.0.1:5432:5432 instead of 0.0.0.0:5432:5432) or use Docker networks without port exposure for test environments. In production, use proper network policies and authentication. - High · Potential SQL Injection in Multi-Tenancy Implementation —
lib/apartment/elevators/ (domain.rb, subdomain.rb, host.rb, host_hash.rb). The apartment gem handles tenant switching through database/schema manipulation. Without proper input validation on tenant identifiers (in elevators like domain.rb, subdomain.rb, host.rb), malicious tenant names could be injected into SQL queries, bypassing tenant isolation. Fix: Implement strict validation and sanitization of tenant identifiers before passing them to database operations. Use parameterized queries and whitelist allowed tenant names. Review the elevator implementations for dynamic SQL construction without proper escaping. - Medium · Hardcoded Configuration Example in Generator Template —
lib/generators/apartment/install/templates/apartment.rb. The install generator creates example configuration files (apartment.rb template) which may contain insecure defaults or examples that developers might use as-is in production without proper hardening. Fix: Review and update the generated template to include security best practices: exclude sensitive tenant operations, implement proper tenant validation, and add security warnings in comments. - Medium · Cross-Tenant Data Leakage Risk —
lib/apartment/tenant.rb, lib/apartment/adapters/abstract_adapter.rb. Multi-tenancy implementations are vulnerable to cross-tenant data exposure if tenant switching logic (in lib/apartment/tenant.rb and adapters) fails to properly isolate database connections or contexts. Race conditions or improper context cleanup could expose one tenant's data to another. Fix: Implement comprehensive tests for tenant isolation under concurrent access scenarios. Add thread-local and fiber-local context management verification. Use database-level schema/user isolation where possible, not just application-level switching. - Medium · Missing Security Headers Configuration —
spec/dummy/config/environments/. The Rails dummy application (spec/dummy) and the gem itself do not appear to enforce security headers like X-Frame-Options, X-Content-Type-Options, or CSP, which could increase XSS vulnerability exposure. Fix: Add security headers middleware or Rails configuration:config.secure_hsts_seconds,config.secure_content_security_policy, and similar settings in production environments. - Low · Outdated Docker Base Images —
docker-compose.yml. PostgreSQL 9.5.12 and MySQL 5.7 are deprecated versions with known vulnerabilities. While acceptable for testing, they should not be used in production-like environments. Fix: Update to supported versions: PostgreSQL 13+ and MySQL 8.0+. Regularly monitor and update base images for security patches. - Low · Missing Input Validation in Tenant Domain Resolution —
lib/apartment/elevators/domain.rb, lib/apartment/elevators/subdomain.rb, lib/apartment/elevators/host.rb. Tenant identification through HTTP headers (Host, subdomains) depends on proper validation. If the elevators don't validate domain/subdomain format, malformed or malicious input could cause unexpected behavior. Fix: Add comprehensive input validation for all tenant identifier sources. Implement whitel
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.