Boot-time grant of permission keys to the Admin role, with retry and a way to wait for it.
Started by PhoenixKit.Supervisor. On start it runs
PhoenixKit.Users.Permissions.auto_grant_new_keys_to_admin/0 and
backfill_media_sub_permissions/0, then waits for PhoenixKit.Dashboard.Registry
to finish loading its tabs (which register and grant their own keys).
A key that hits a database error is not dropped until the next restart: it
is kept as pending and tried again after a backoff (1 second, doubled on each
attempt, 6 attempts by default: about 63 seconds in all). The retry is a server-side timer rather than
a retry on the next access because nothing reads these grants lazily — a
request that needs the key would simply be denied — and because a transient
failure (pool exhaustion, a database restart) usually clears within seconds.
Each grant is idempotent and sets its auto_granted_perm:<key> flag only on
success, so a retry never overrides an Owner's later revocation. A failure
reported later from outside the sweep (Permissions.auto_grant_to_admin_roles/1
called by the tab registry or by register_custom_key/2) joins the same retry.
Only a database error (an exception or an exit) is retried. A rejected
changeset, or a missing sandbox owner (a test suite that already switched to
:manual), would fail again, so it is logged once and counted as failed.
Each key logs at error level on its first pass, at warning on a retry, and
giving up logs one error with the keys still missing.
A key given up on stays reported (by await/2) until the next restart: the
server cannot see a grant made behind its back.
The server is :permanent: every step that touches the database rescues, so
it does not crash-loop and take PhoenixKit.Supervisor with it.
PhoenixKit.Users.Permissions.await_admin_grants/1 waits for the result; the
retries take about 63 seconds, so a shorter timeout can return :timeout
while they are still running.
Summary
Functions
Queues a key (or :media_subs) whose grant just failed for a retry. A no-op
when the server is not running.
Options: :name, :retry_base_ms (first backoff, default 1000),
:max_attempts (default 6) and :sync_registry (default true),
:sweep_fun and :registry_fun (test seams for the sweep and the registry wait).
Functions
@spec await(timeout(), GenServer.server()) :: :ok | {:error, :timeout | {:failed, list()} | {:exit, term()}}
@spec report_failure(String.t() | :media_subs, GenServer.server()) :: :ok
Queues a key (or :media_subs) whose grant just failed for a retry. A no-op
when the server is not running.
Options: :name, :retry_base_ms (first backoff, default 1000),
:max_attempts (default 6) and :sync_registry (default true),
:sweep_fun and :registry_fun (test seams for the sweep and the registry wait).