# 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 runs `bundle exec rspec`) - `bin/rails db:prepare` — create/migrate DB (also `db:setup`, `db:migrate`) - `bin/rails ldap:setup` — reset LDAP dir and seed dev entries - `bin/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 with `bin/importmap pin --download`. - **Auth:** Devise + `devise_ldap_authenticatable`; `authentication_keys = [:cn]`, custom SSHA512 password builder, admin bind. Admin-only mounts (`/jobs` MissionControl, `/flipper` Flipper UI) are gated by `authenticate :user, ->(u) { u.is_admin? }`. - **Services:** `app/services/` uses `ApplicationService.call(**args)` with namespaced `*ManagerService` bases (e.g. `UserManagerService`) and verb-named operations (`UserManager::CreateAccount`, `NostrManager::PublishEvent`, `BtcpayManager::*`). - **Models:** `User` is central; `Setting` uses rails-settings-cached with one concern per service in `app/models/concerns/settings/`. LNDHub models connect to a separate DB (`LndhubBase` `establish_connection :lndhub`). LDAP is accessed via `LdapService`/ `LdapManagerService`, not ActiveRecord. - **Jobs:** `app/jobs/` inherit `ApplicationJob < ActiveJob::Base`; backend is **Solid Queue** (`config.active_job.queue_adapter = :solid_queue`, separate `queue` DB in prod). `config/recurring.yml` has no active scheduled jobs. Ignore `config/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`); `SERVICES` constant is loaded from `config/services.yml` in `config/initializers/service_details.rb`. Custom LDAP schema attributes are applied from `schemas/ldap/*.ldif` by `lib/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 with `allow(Devise::LDAP::Adapter).to receive(:get_ldap_param).with(user.cn, :admin).and_return(["true"])`. - **Services** (`spec/services/`) are tagged `type: :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 in `spec/fixtures/`; factories in `spec/factories/`. ## Notes & gotchas - Generators (`config/application.rb`): ERB templates, RSpec, no stylesheets, factory_bot with `_factory` suffix dir `spec/factories`. - `gitno/` and `extras/strfry/` are scratch/infra (Deno Nostr relay policies) — not app code; ignore. - Don't add code comments unless asked (matches repo style); `frozen_string_literal` is inconsistent. - Default dev login: username `admin` / password `admin is admin`.