# `MishkaCmsCore.Install.Bootstrap`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/install/bootstrap.ex#L5)

Runs the first-run install: creates every permission name of `permissions/0` that is missing,
grants `"*"` to the master user (auto-confirmed, no email), creates the first (master) site,
installs the chosen Chelekom components, writes the site's starter content — the theme, the
header, the footer, the `site-main` layout and the home page
(`MishkaCmsCore.Install.StarterContent.seed!/2`) — and its sign-in pages
(`MishkaCmsCore.Install.StarterContent.ensure_auth!/1`), compiles the new site's runtime —
exactly as a normal boot would — and flips the install flag. A name already stored is left as it
is, so re-running after seeds is safe.

THE STARTER IS NOT A CHOICE. Every page of a site, an extension's included, is drawn in the
`site-main` layout (`MishkaCmsCore.Runtime.SiteLayout`), so a site without it has no frame for
any of them; and a first install whose `/` answers nothing is a broken one. The installer's box
says so and cannot be unticked.

Everything runs `authorize?: false` because no privileged actor exists yet — which is why
this is reachable only while the CMS is uninstalled (`MishkaCms.Plugs.InstallGate`).

The install runs in ordered phases (`@steps`); the installer renders one progress row per entry,
so adding a phase here (with a matching `run_step/3` clause) makes it appear in the UI
automatically.

The master user is created via the existing `register_with_password` strategy action (which mints
the token and hashes the password); with no tenant the user stays `site_id: nil` (a master), and
`authorize?: false` relaxes the master-creation policy. The master is confirmed in-place and the
changeset is tagged with `mishka_install: true` so the confirmation sender skips the email (see
`MishkaCmsCore.Accounts.User.Senders.SendNewUserConfirmationEmail`).

Essential settings are written through the `Settings` façade with the category/source declared for
each key in the registry, so each lands on the right tab of the settings page; unregistered keys
are ignored.

The site's languages (`:languages`, codes `MishkaCmsCore.Runtime.Languages.valid?/1` accepts, the
first being the primary) are saved to the new site's own `site.languages` before any page is
written, so every page the install writes answers each of them. A run given none leaves the site
reading the global list. How its addresses name them (`:language_urls`, one of
`MishkaCmsCore.Runtime.Languages.url_modes/0`) is saved beside them to its own
`site.language_urls`; a run given none leaves the site reading the global setting.

See `MishkaCmsCore.Install`.

# `params`

```elixir
@type params() :: %{
  :email =&gt; String.t(),
  :password =&gt; String.t(),
  :site_name =&gt; String.t(),
  :host =&gt; String.t(),
  optional(:component_selection) =&gt; map(),
  optional(:languages) =&gt; [String.t()],
  optional(:language_urls) =&gt; String.t(),
  optional(:settings) =&gt; %{optional(String.t()) =&gt; term()},
  optional(:extensions) =&gt; [String.t()],
  optional(:extension_answers) =&gt; %{optional(String.t()) =&gt; map()}
}
```

# `ensure_permissions`

```elixir
@spec ensure_permissions([{String.t(), String.t()}]) ::
  {:ok,
   %{
     stored: [MishkaCmsCore.Accounts.Permission.t()],
     created: [MishkaCmsCore.Accounts.Permission.t()]
   }}
  | {:error, term()}
```

Creates each name of `permissions` (the core's, `permissions/0`, when none are given) that is not
stored yet. Returns every one of them, stored before or now, and the ones this call created. A
stored name keeps its row and its description.

# `permissions`

```elixir
@spec permissions() :: [{String.t(), String.t()}]
```

Every permission name the core install creates, with its description. An extension's own names
are not here: its provision manifest ships them, and installing it creates them
(`MishkaCmsCore.Runtime.ExtensionProvision`).

# `run`

```elixir
@spec run(params(), (term() -&gt; any())) ::
  {:ok, %{user: map(), site: map()}} | {:error, term()}
```

Runs the install phases in order, calling `progress` with `{step_id, status, detail}` as each phase
starts (`:running`), emits a line (`:log`), finishes (`:done`) or fails (`:error`). Returns
`{:ok, %{user: user, site: site}}` or `{:error, reason}` from the first failing phase. `run/1` keeps
the silent behaviour for non-interactive callers (seeds, tests).

# `steps`

```elixir
@spec steps() :: [{atom(), String.t()}]
```

The ordered install phases as `{id, label}` — one progress row per entry.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
