Phoenix Integration Spine

Copy Markdown View Source

This is the first-hour path for a Phoenix host app: from dependencies through supervision, config, Plug, your first runtime evaluation, and a lifecycle-honest flag create. Budget about fifteen minutes if you already have Postgres and a running Phoenix app.

For the payload-first mental model (tests, simulations, and pure evaluation), see Evaluation. This spine optimizes for the path most teams take in production: snapshot cache + keyed lookup.

1. Before you start

Add the packages you need (see Installation):

defp deps do
  [
    {:rulestead, "~> 0.1"},
    {:rulestead_admin, "~> 0.1"}  # optional operator UI
  ]
end

Then install and migrate:

mix deps.get
mix rulestead.install
mix ecto.migrate

mix rulestead.install writes host config, injects Rulestead.Plug into your endpoint, and (when enabled) scaffolds the admin mount. It does not patch your host application.ex — runtime supervision starts with the :rulestead OTP application (next section).

2. How Rulestead runs in your BEAM

When {:rulestead, ...} is in your deps, the :rulestead application starts Rulestead.Application. That supervisor owns the local snapshot runtime, not your host app's root supervisor.

Typical children include:

  • Rulestead.Runtime.Supervisor — snapshot cache and keyed lookup
  • Rulestead.Analytics.Batcher — bounded analytics batching
  • Rulestead.Admin.StaleTracker — lifecycle hygiene signals for operators

You do not add these modules to MyApp.Application children yourself. If the :rulestead app is not running, keyed runtime calls will not see a warm cache.

3. Host config after install

The installer writes config/rulestead.exs and adds import_config "rulestead.exs" to your host config/config.exs. The shape matches what the installer generates (abbreviated):

import Config

config :rulestead, :store, Rulestead.Store.Ecto

config :rulestead, Rulestead.Repo,
  repo: MyApp.Repo

config :rulestead, :host,
  environment_key: "dev",
  plug: [
    context_assign: :rulestead_context,
    targeting_key_sources: [
      session: "targeting_key",
      cookie: "rulestead_targeting_key",
      header: "x-rulestead-targeting-key"
    ]
  ],
  runtime: [
    api: Rulestead.Runtime,
    notifier: Rulestead.Runtime.Notifier.PhoenixPubSub,
    pubsub: MyApp.PubSub,
    pubsub_topic: "rulestead:runtime_snapshot"
  ]

Tune environment_key per deploy. Plug and runtime keys must stay aligned with how you build %Rulestead.Context{} in request handlers.

4. Request boundary: Plug

The installer places Rulestead.Plug in your endpoint after Plug.Telemetry — for example:

plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
plug Rulestead.Plug

The plug assigns a normalized %Rulestead.Context{} to conn.assigns[:rulestead_context]. It does not evaluate flags by itself.

For LiveView mount, Oban workers, and explicit source lists, continue in Context Propagation — do not duplicate that machinery here.

5. First runtime evaluation

In a controller (or Plug-aware context), read the assign the plug set:

context = conn.assigns[:rulestead_context]

{:ok, enabled?} =
  Rulestead.Runtime.enabled?("dev", "checkout_v2", context)

Rulestead.Runtime looks up the authored flag in the local snapshot cache for the environment key. It is the supported Phoenix hot path when you already run the snapshot runtime.

For unit tests, simulations, or inspecting one payload in isolation, use payload-first Rulestead.evaluate/3 instead — see Evaluation. The root Rulestead projection helpers (enabled?/2, get_variant/2) take flag payload + context, not a string key on %Plug.Conn{}.

6. Create your first flag (lifecycle required)

Every flag must be born with explicit lifecycle metadata. Rulestead enforces this at creation time.

Record at minimum:

  • owner_ref — stable host-owned reference (team-growth, svc-checkout, a person id your systems already understand). Rulestead does not maintain a user or team directory.
  • expected_expiration — review horizon as a date (or the lifecycle fields your authoring surface maps to it).

In the mounted admin UI, the create form requires these fields before save. If you author through store APIs, pass the same metadata — missing owner or expiration should fail closed rather than silently defaulting.

Honest posture:

  • Owner truth stays in your systems; Rulestead stores bounded references.
  • Lifecycle guidance is advisory for operators — it does not change hot-path evaluation semantics.

Full lifecycle flows (review queues, archive readiness, cleanup) live in Flag Lifecycle.

7. Optional: mount the admin UI

If you installed rulestead_admin, the installer adds a router mount similar to:

use RulesteadAdmin.Router

scope "/admin", MyAppWeb do
  pipe_through :browser
  rulestead_admin "/flags", policy: MyApp.AdminPolicy
end

Provide the host policy: module and session keys documented in rulestead_admin/README.md. Rulestead does not bundle authentication — your app owns identity.

8. Next steps