Skip to content

Going to production

Sundae and Scout work on your laptop. Shipping them means two things the conductor and localhost were letting you skip: real mail and real security. This chapter is the pre-launch checklist.

Choose a mail transport

In development you used the Action Mailbox conductor and the in-app console — no mail server. In production, inbound mail arrives through an Action Mailbox ingress and replies go out over Action Mailer SMTP. There are two paths:

  • An email provider (recommended, config-only) — point the ingress at the provider's inbound webhook and SMTP at its servers. No infrastructure to run, and the provider handles SPF/DKIM signing.
  • Self-hosting an MTA — run the Postfix + OpenDKIM image Protege scaffolds with rails g protege:postfix. More control, but you own deliverability (rDNS, port 25, reputation).

Either way, publish the DNS records for thescoop.com shown on its Domains page. The full walkthrough for both paths — config, DNS table, and verification — is in Mail transport.

The security checklist

Protege enforces a lot for you — routing, the recursion guard, reply identity, bounded loops — but a few things are yours to own before you expose an agent to the world. (The full model is in Security.)

  • Wrap the mount in authentication. This is the big one. The engine ships no auth, so an unwrapped mount Protege::Engine => "/protege" exposes every agent, thread, and trace to anyone who finds the URL. With Devise, its routing helper does it:

    ruby
    # config/routes.rb (Devise)
    authenticate :user, ->(u) { u.admin? } do
      mount Protege::Engine => "/protege"
    end

    With any other auth (including Rails 8's built-in generator), a routes constraint over your session works the same way:

    ruby
    # config/routes.rb (roll-your-own sessions)
    admin = ->(request) { Session.find_by(id: request.cookie_jar.signed[:session_id])&.user&.admin? }
    constraints(admin) { mount Protege::Engine => "/protege" }
  • Set the inbound access ceiling. Decide the org-wide boundary with config.inbound_access, and confirm the per-agent rules from chapter 5 are in place — Scout locked to *@thescoop.com, Sundae open to customers.

  • Review each tool's blast radius. A tool is your app's full power in the agent's hands. Re-read your tools with an adversarial eye: does ProcessRefundTool refund any order, or only one that warrants it? Treat customer email as untrusted input and enforce authorization in the tool, not in the prompt.

  • Confirm attachment limits (config.attachment_policy) fit what you actually want to accept.

  • Give bounce mail a real sender. The engine's bounce notices (unrouted, access-denied, attachment rejected) go out through Action Mailer with Rails' placeholder from@example.com unless you set one — use an address on your registered domain:

    ruby
    # config/environments/production.rb
    config.action_mailer.default_options = { from: "no-reply@thescoop.com" }

The deploy checklist

What every deploy (first and subsequent) must run, in order:

  1. bin/rails db:migrate — the engine's migrations ride along with your app's.
  2. bin/rails protege:toolkits:sync — keeps the system toolkits current, so All Tools includes any tool you shipped in this release and agents created later still auto-attach Default Tools. Idempotent; the seeds pattern (Rails.application.eager_load! then Protege::SystemToolkits.sync! in db/seeds.rb, before any agent creation) works too.
  3. Run your job backend. Everything asynchronous — inference runs, scheduled responsibilities — moves through Active Job. On the Rails 8 default stack that means Solid Queue is running (bin/jobs, or inside Puma via SOLID_QUEUE_IN_PUMA), which also executes the config/recurring.yml tick that fires scheduled work. On Sidekiq/GoodJob, remember the tick needs their cron facility (see Scheduled work).

Domain records (agents, toolkits, responsibilities, access rules) live in the database and carry over between deploys — only the sync above needs re-running.

You shipped The Scoop

That's the whole arc. Starting from rails new, you:

  • mounted the engine and registered thescoop.com;
  • hired Sundae (a CustomerServiceAgent record) and gave her tools to look up orders, issue refunds, check stock, and handle photos;
  • gave her the right context and memory with resolvers;
  • hired Scout (an OperationsAgent record), scoped her tools and senders, and put her on a schedule;
  • learned to watch both agents through the introspection panel and tracing.

Where to go from here:

  • Extensions — the authoring reference for tools, providers, resolvers, and hooks.
  • Reference — every config option, model, and generator.
  • Architecture — how the gateway, harness, and scheduler fit together under the hood.