Skip to content

MCP & Settings credentials 500 without Active Record encryption keys #387

Description

@TonsOfFun

Part of an ActiveAgent + actionagent dashboard functional review (multi-agent, adversarially verified). Severity: 🟠 Major.

ActionAgent.encrypt_credentials defaults to true, so ApiKey/ProviderKey declare encrypts, but the engine ships no fallback for a host that hasn't run rails db:encryption:init — including its own reference host, test/dummy. Result: the advertised MCP flow (Settings → API Keys → connect a client to <mount>/mcp) is dead on arrival and every Settings credential write 500s on a bare mount. The app carried a fallback initializer that was never ported.

Findings

Engine MCP facade and API-key issuance 500 on any host without Active Record encryption keys; app-side fallback initializer never ported

  • Where: actionagent/lib/action_agent/engine.rb:46 · severity: major · kind: drift · repo: activeagent
  • What breaks: The engine's advertised MCP flow (Settings -> API Keys -> connect an MCP client to /mcp with a Bearer key) is dead on arrival on any host that has not run rails db:encryption:init — including the engine's own reference host, test/dummy. ApiKey declares encrypts :token, deterministic: true gated only on ActionAgent.encrypt_credentials, which defaults to true (lib/action_agent.rb:395), and the dummy configures no encryption keys (no initializer; config/initializers contains only content_security_policy.rb and filter_parameter_logging.rb). POST /api/api_keys raises Missing Active Record encryption credential: active_record_encryption.deterministic_key -> HTML 500, so no key can ever be created; and because ApiKey.authenticate's find_by(token:) also encrypts the lookup value, EVERY authenticated POST /mcp raises the same error inside the authenticate_api_key! before_action (mcp_controller.rb:74-86), escaping the action's rescue and returning an HTML 500 instead of any JSON error. The platform app fixed exactly this with config/initializers/active_record_encryption.rb (derives fallback keys from the app secret, ENV override), which is why /mcp works on activeagents.ai; the engine/dummy never got the equivalent — drift, engine behind. Also drift: the app has test/controllers/api/mcp_controller_test.rb (10 cases covering auth, tools/list, tools/call, quotas, cross-account isolation, resources/read); the engine has zero tests for its MCP facade.
  • Evidence: Live: curl --noproxy localhost -X POST http://localhost:3001/activeagents/api/api_keys -H 'Content-Type: application/json' -d '{"name":"mcp-probe"}' -> 500 HTML, exception message "Missing Active Record encryption credential: active_record_encryption.deterministic_key". curl -X POST http://localhost:3001/activeagents/mcp -H 'Authorization: Bearer aa_testtoken123' ... -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}' -> 500 HTML with the same message (no-auth request correctly returns 401 JSON). Code: actionagent/app/models/action_agent/api_key.rb:19 (encrypts ... if ActionAgent.encrypt_credentials), actionagent/lib/action_agent.rb:395 (@encrypt_credentials = true default), actionagent/app/controllers/action_agent/api/mcp_controller.rb:74-86 (authenticate in before_action, outside create's rescue). Sibling fix that never crossed over: activeagents config/initializers/active_record_encryption.rb:13-20 — the same probe against the app worked (POST /api/api_keys returned a token; full JSON-RPC session verified live at :3000). Test drift: activeagents test/controllers/api/mcp_controller_test.rb exists (10 tests); grep -rl 'jsonrpc\|MCPController' actionagent/test/ matches nothing.
  • Suggested fix: Port the app's fallback into the engine: add an engine initializer in actionagent/lib/action_agent/engine.rb (e.g. "action_agent.active_record_encryption", before :load_config_initializers completes on the host) that, when ActionAgent.encrypt_credentials is true and app.config.active_record.encryption.primary_key/deterministic_key/key_derivation_salt are all unset, derives them via Rails.application.key_generator.generate_key("active_record_encryption.", 32).unpack1("H*") with ACTIVE_RECORD_ENCRYPTION_* ENV overrides — mirroring activeagents/config/initializers/active_record_encryption.rb:13-20 (never override host-provided keys). Alternatively/additionally have the install generator write that initializer into the host, and add one to test/dummy. Defense in depth: rescue ActiveRecord::Encryption::Errors (Configuration) in ApiKey.authenticate and Api::ApiKeysController#create to return an actionable JSON error ("Active Record encryption is not configured; run rails db:encryption:init or set ActionAgent.encrypt_credentials = false") instead of an HTML 500. Port activeagents/test/controllers/api/mcp_controller_test.rb (10 cases) to actionagent/test/.

Every Settings credential write 500s on the engine's dummy host: Active Record encryption keys are never configured

  • Where: test/dummy/config/environments/development.rb:3 · severity: major · kind: bug · repo: activeagent
  • What breaks: ActionAgent.encrypt_credentials defaults to true (actionagent/lib/action_agent.rb:395), which makes ApiKey encrypts :token, deterministic: true (api_key.rb:19) and ProviderKey encrypts :credential (provider_key.rb:23), but the engine's own reference host test/dummy has no active_record.encryption keys anywhere (no initializer, nothing in config/environments). Result: on the running dummy, GET index endpoints work but POST /api/api_keys and POST /api/provider_keys both raise ActiveRecord::Encryption::Errors::Configuration — an unhandled 500 returning an HTML exception page. The whole Settings -> API Keys / Provider Keys create flow is dead in the shipped dummy (SettingsView surfaces only the generic 'Could not create the API key.'), the token-shown-once flow cannot be exercised at all, and any host app that skips rails db:encryption:init hits the same raw 500 instead of an actionable API error. The app side works (encryption configured): live create returned the full token once, masked thereafter, and the token authenticated telemetry ingest.
  • Evidence: Live: curl --noproxy localhost -X POST http://localhost:3001/activeagents/api/api_keys -H 'Content-Type: application/json' -d '{"name":"review-probe"}' returns the Rails diagnostics page 'ActiveRecord::Encryption::Errors::Configuration in ActionAgent::Api::ApiKeysController#create (Missing Active Record encryption credential: active_record_encryption.deterministic_key)'; same POST to /activeagents/api/provider_keys returns HTTP 500 'Missing Active Record encryption credential: active_record_encryption.primary_key'. Code: actionagent/lib/action_agent.rb:395 (@encrypt_credentials = true default), actionagent/app/models/action_agent/api_key.rb:19, actionagent/app/models/action_agent/provider_key.rb:23; grep of /home/user/activeagent/test/dummy/config finds zero encryption configuration. Contrast (working app): POST http://localhost:3000/api/api_keys returned 201 with token aa_jfke…Sihk shown once, index masked, and Bearer use on POST /api/v1/traces returned 202.
  • Suggested fix: Two-part fix: (1) Make the dummy host exercise the default encrypted path by adding Active Record encryption keys to test/dummy config (e.g. config.active_record.encryption.primary_key/deterministic_key/key_derivation_salt with fixed dev-only values in development.rb, mirroring what Rails' db:encryption:init would produce). (2) In actionagent/app/controllers/action_agent/api/base_controller.rb, add rescue_from ActiveRecord::Encryption::Errors::Base returning a JSON 500/503 that tells operators to run rails db:encryption:init or set ActionAgent.encrypt_credentials = false — matching the guidance the install generator already prints (install_generator.rb:112-113) — so misconfigured hosts get an actionable API error instead of an HTML diagnostics page the SettingsView collapses into a generic failure message.

Verification

Each finding above was produced by a dedicated per-feature review agent, then confirmed by an independent adversarial verifier (all rated high-confidence; zero rejected in this set). File:line citations are against the current main/HEAD of each repo; many were reproduced live against a booted dashboard.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions