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" endWith 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
ProcessRefundToolrefund 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.comunless 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:
bin/rails db:migrate— the engine's migrations ride along with your app's.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!thenProtege::SystemToolkits.sync!indb/seeds.rb, before any agent creation) works too.- 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 viaSOLID_QUEUE_IN_PUMA), which also executes theconfig/recurring.ymltick 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
CustomerServiceAgentrecord) 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
OperationsAgentrecord), 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.