4.4 KiB
AGENTS.md
Akkounts is a Rails 8 monolith for managing Kosmos/LDAP user accounts. It authenticates against an LDAP directory via Devise and integrates with ejabberd, Discourse, Mastodon, remoteStorage, Nostr, LNDHub, and BTCPay.
Development environment
Development runs in Docker Compose — run all commands against the web container.
Start services: docker compose up (web, ldap, redis, minio, liquor-cabinet, strfry).
The web service runs bin/dev (foreman: Puma + Tailwind CSS watcher) and embeds
Solid Queue workers (SOLID_QUEUE_IN_PUMA=true).
First-time LDAP setup (after creating the 389ds backend once):
docker compose exec ldap dsconf localhost backend create --suffix="dc=kosmos,dc=org" --be_name="dev"
then docker compose run web bin/rails ldap:setup.
Common commands (prefix with docker compose exec web)
bin/rspec— run the test suite (CI runsbundle exec rspec)bin/rails db:prepare— create/migrate DB (alsodb:setup,db:migrate)bin/rails ldap:setup— reset LDAP dir and seed dev entriesbin/rails css:build— build Tailwind CSS (auto-watched in dev)bin/rails lndhub:generate_wallets,invitations:generate_for_all_users[N]bin/jobs— Solid Queue CLI
Tests use the test env explicitly: docker compose exec -e RAILS_ENV=test web bin/rspec.
There is no enforced linter — a bin/rubocop binstub exists but no .rubocop.yml
(rubocop is a transitive gem, not in the Gemfile); CI does not lint.
Stack & conventions
- Assets/views: ERB + ViewComponent (
app/components/), Tailwind built via Bun + importmap (config/importmap.rb); pin JS deps withbin/importmap pin <pkg> --download. - Auth: Devise +
devise_ldap_authenticatable;authentication_keys = [:cn], custom SSHA512 password builder, admin bind. Admin-only mounts (/jobsMissionControl,/flipperFlipper UI) are gated byauthenticate :user, ->(u) { u.is_admin? }. - Services:
app/services/usesApplicationService.call(**args)with namespaced*ManagerServicebases (e.g.UserManagerService) and verb-named operations (UserManager::CreateAccount,NostrManager::PublishEvent,BtcpayManager::*). - Models:
Useris central;Settinguses rails-settings-cached with one concern per service inapp/models/concerns/settings/. LNDHub models connect to a separate DB (LndhubBaseestablish_connection :lndhub). LDAP is accessed viaLdapService/LdapManagerService, not ActiveRecord. - Jobs:
app/jobs/inheritApplicationJob < ActiveJob::Base; backend is Solid Queue (config.active_job.queue_adapter = :solid_queue, separatequeueDB in prod).config/recurring.ymlhas no active scheduled jobs. Ignoreconfig/sidekiq.yml— it's stale (sidekiq is not in the Gemfile; the README mention is outdated). - Config: env-var driven (
.env.example/.env.development/.env.test);SERVICESconstant is loaded fromconfig/services.ymlinconfig/initializers/service_details.rb. Custom LDAP schema attributes are applied fromschemas/ldap/*.ldifbylib/tasks/ldap.rake.
Testing conventions (spec/)
spec/rails_helper.rb includes FactoryBot, Devise ControllerHelpers (controller specs),
Warden Test::Helpers, DatabaseCleaner (transactional fixtures off), ViewComponent
TestHelpers + Capybara matchers, and ActiveJob::TestHelper for type: :job.
- Feature specs (
spec/features/,type: :feature): Capybara rack-test driver (no JS). Log in with Warden:login_as user, scope: :user; stub admin withallow(Devise::LDAP::Adapter).to receive(:get_ldap_param).with(user.cn, :admin).and_return(["true"]). - Services (
spec/services/) are taggedtype: :model; exercise#call/service.send(:private)and assert on DB rows /enqueued_jobs. - Request/component/mailer/job/model/helper specs in matching
spec/subdirs. Fixtures live inspec/fixtures/; factories inspec/factories/. spec_helper.rbreloads routes before eachtype: :controllerspec (Devise + Rails 8 workaround).
Notes & gotchas
- Generators (
config/application.rb): ERB templates, RSpec, no stylesheets, factory_bot with_factorysuffix dirspec/factories. gitno/andextras/strfry/are scratch/infra (Deno Nostr relay policies) — not app code; ignore.- Don't add code comments unless asked (matches repo style);
frozen_string_literalis inconsistent. - Default dev login: username
admin/ passwordadmin is admin.