API Reference Mishka CMS Core v#0.0.1

Copy Markdown View Source

Modules

MishkaCms Accounts Domain - Complete user authentication and authorization system.

The ways into an account a site's admin — or a master, for any site or every site — can close: :sign_in, :register, :reset (forgot password), :password (signing in or registering with a password) and :magic_link (sign-in links). Set on the settings page's Security tab, the last two as switches in its "Sign-in methods" table; see definitions/0.

A way into an account that MishkaCmsCore.Accounts.AuthGate keeps closed: :action is the way — :sign_in, :register, :reset or :magic_link — and :site_id the site it was closed for (nil for a master's). Rendered by the JSON:API as a 403 auth_closed and by the sign-in pages as their own notice (see MishkaCmsCore.Accounts.AuthGate.describe/1).

How often someone may sign in with a password, ask for a password reset, ask for a sign-in link and try their current password to change it: the backend and the keys MishkaCmsCore.Accounts.User gives ash_rate_limiter for those four actions, and what to tell whoever was refused.

Whether the account a record names is a master's, for every record in one read.

The captcha (MishkaCmsCore.Captcha) in the actions a visitor fills a form for: sign_in_with_password, register_with_password, request_password_reset_token and request_magic_link, each taking the token as its captcha_token argument. Checked in the action, so the sign-in pages and the /api/auth routes ask it alike.

A form sent without the captcha its site asks (reason: :missing) or with one that did not pass (reason: :failed): :form is the form — :sign_in, :register, :reset_request, :magic_link, or an extension's own — and :site_id its site (nil for a master's). Shown by the sign-in pages and the /api/auth routes as their own notice; see MishkaCmsCore.Accounts.CaptchaCheck.describe/1.

Tells plugins an account turned its authenticator on or off, or made recovery codes, once the write succeeds: "auth:second_factor:enabled" and "auth:recovery_codes:generated" with %{user_id:, site_id:}, "auth:second_factor:disabled" with %{user_id:, site_id:, by:} — by the id of the admin who turned it off, nil when the account did. All with return: false.

Debug change for troubleshooting token generation and tenant issues.

Refuses a master an address a site's account already holds.

Revokes every token a user holds once a write that changed their password has committed, through the log_out_everywhere add-on's own action, and hands the device that made the change a new one.

Puts the ids of the users holding this permission into the changeset, before the write.

Asks the action's :code argument to be a current code from an authenticator app before the write, and spends it (MishkaCmsCore.Accounts.SecondFactor.use_code/3): the account's own with of: :account, the default, or the admin's running the action with of: :actor — who then has to have set one up for their own account first. A refusal is MishkaCmsCore.Accounts.SecondFactor.Needed, or the rate limiter's.

Whether an actor holds what a gate requires — the one matcher every policy, plug, page guard, sidebar entry, widget, setting and notification audience asks.

True while what an account holds — its site, its permissions — is reached from that account by a caller who may not see it. Two ways reach it

Policy check: passes unless the way in named by :action — :register or :reset — is closed for whoever the action is for (MishkaCmsCore.Accounts.AuthGate). Put it on the action, above the AshAuthentication bypass, with the gate's refusal as the policy's error

True when an account action would change which permissions the account holds, through its permission_ids argument.

This check is true if the access is coming from a relationship load.

Policy check: passes unless the action would create a new account on a site that is offline.

True when what an action changes holds nothing beyond what the actor holds — the grant ceiling (MishkaCmsCore.Accounts.Validations.GrantWithinActorScope) asked of the account or the row an action changes, by the same covering rule (MishkaCmsCore.Accounts.Checks.AccessPermissions.beyond/2).

Helper functions for account management including user role checks and email domain configuration for multi-tenant environments.

The name a signed-in visitor's live sockets answer to, and the broadcast that cuts them.

Cuts a user's live sockets after any write that can shrink what they may reach or end their being signed in at all — see MishkaCmsCore.Accounts.LiveSockets for what "cut" means here.

Defines system permissions for role-based access control.

One AshAuthentication strategy for every sign-in provider, whichever the installed plugins are: the core names none of them.

Both phases of a provider's sign-in: the provider the request path names — kept in the session for the callback, which every provider shares — is looked up among the "auth:providers" plugins, refused unless the site the request reached offers it, and filled into the strategy — its Assent strategy, Assent's own defaults for it, what the plugin overrides, the site's keys and the callback on the site's address — before AshAuthentication's OAuth2.Plug runs the phase.

An account's one-time recovery codes, for signing in when its authenticator app is lost — the store of AshAuthentication's recovery_code strategy on MishkaCmsCore.Accounts.User. See MishkaCmsCore.Accounts.SecondFactor.

An authenticator app as a second step after the password, for an account that has set one up: AshAuthentication's totp strategy on MishkaCmsCore.Accounts.User. An account without one signs in with its password alone, as before.

A sign-in MishkaCmsCore.Accounts.SecondFactor refused: :reason is :required (no code was given), :invalid (the code did not match, or was used already) or :password (a way in that cannot ask for a code, such as a sign-in link). Rendered by the JSON:API as a 401 second_factor_<reason> and by the sign-in page as its second step or its notice.

The code step of a sign-in that could not ask for the code itself — one with a provider such as Google or GitHub.

Signing in with another service's account as mishka_installer plugins on "auth:providers", the way MishkaCmsCore.Captcha.Provider makes captchas. The core names no provider: each ships as its own package (mishka_cms_auth_google, mishka_cms_auth_github, …) whose application starts its plugin, so a host that adds the dependency has the provider on every site's Security tab.

The buttons of the services a site signs in with, for the sign-in and registration pages: one link per provider the site offers (MishkaCmsCore.Accounts.SignInProvider.available/1), to its provider's request route, which sends the visitor to the provider. A site that offers none draws its empty slot instead, and no divider. Every runtime template can call it (MishkaCmsCore.Runtime.LiveViewHelpers.imports/1)

What a sign-in with a provider — Google, GitHub, any SignInProvider plugin — may do, on each provider's register action on MishkaCmsCore.Accounts.User, beside AshAuthentication's own rules (AshAuthentication.Strategy.OAuth2.IdentityChange)

A sign-in with a provider that MishkaCmsCore.Accounts.SocialSignIn turned away: :reason is :unverified_email (the provider did not vouch for the email), :no_account (no account has it and the site makes none this way) or :no_master (no master has it). Told to the visitor by the sign-in page (see MishkaCmsCore.Accounts.SocialSignIn.describe/1).

Manages authentication tokens in the MishkaCMS system.

The dashboard of a site that requires two-step sign-in (MishkaCmsCore.Accounts.SecondFactor.policy/1), for an admin who has not set an authenticator up (SecondFactor.owed?/1): every page but the settings page's Preferences tab sends them there, with a flash saying why. An on_mount of every admin page — see MishkaCmsCore.ExtensionLiveView.

Manages user accounts and authentication in the MishkaCMS system.

A mishka_installer plugin on "auth:recovery_code:used": emails the account that one of its recovery codes signed someone in, and how many are left, so whoever did not do it hears of it. Sent as the sign-in emails are (MishkaCmsCore.Accounts.Helper.send_auth_email/3), from the account's own site, the installation's for a master. Registered in MishkaCmsCore.Application.

A mishka_installer plugin on "auth:second_factor:disabled": emails the account that its two-step sign-in was turned off — by an admin, or by the account itself — so whoever did not do it hears of it. Sent as the sign-in emails are (MishkaCmsCore.Accounts.Helper.send_auth_email/3), from the account's own site, the installation's for a master. Registered in MishkaCmsCore.Application.

A mishka_installer plugin on "AuthController.Success.SignIn": an admin of a site that recommends two-step sign-in (MishkaCmsCore.Accounts.SecondFactor.reminded?/1), and has no authenticator, is sent a notification at every sign-in, linking to where it is set up. The dashboard says the same (MishkaCmsCoreWeb.Live.Admin.Dashboard.Home.Index). Registered in MishkaCmsCore.Application.

Sends a sign-in link, through MishkaCmsCore.Mail from the site the request came to (see MishkaCmsCore.Accounts.Helper.send_auth_email/3). Nothing is sent where the site cannot be told apart or may not email (Helper.may_email?/2).

Sends a new user the link to confirm their email address, through MishkaCmsCore.Mail from the site they registered on (see MishkaCmsCore.Accounts.Helper.send_auth_email/3).

Sends a password reset link, through MishkaCmsCore.Mail from the site the request came to (see MishkaCmsCore.Accounts.Helper.send_auth_email/3).

One sign-in provider an account has used: AshAuthentication's user identity, a row per provider per account, keyed on the provider's stable user id (uid, its sub) — never the email. Google, GitHub and Slack can all sit on one account. See MishkaCmsCore.Accounts.SignInProvider.

Manages the many-to-many relationship between users and permissions.

Passes for an action run for a site (a tenant), and fails for one run with none — a master's. A where: condition, so a change written for a site's accounts leaves masters' to another

Passes for an action a visitor's request runs, and fails for one run with authorize?: false — the installer, the seeds, the console. A where: condition, so a change meant for visitors leaves those alone

Refuses a permission grant that would hand out more than the granting actor holds.

Validates email addresses according to RFC 5322 standards.

Synthetic, read-only resource representing one unified activity-log entry, and the gervaz timeline table over it.

Behaviour for activity-feed data sources. Each adapter answers four questions

Ash calculation that exposes :resource_label (string) on a paper-trail version resource. Each PaperTrail mixin passes a literal label so the same name appears in the activity feed and any other Ash consumer (API, GraphQL) without that consumer having to inspect the source module name.

Ash calculation that exposes :severity (atom) on a resource carrying an action name — every paper-trailed version resource, and the event logs the activity timeline draws. Delegates to MishkaCmsCore.Activity.Helpers.severity_for/1 so the same mapping is used in the live broadcast path and in the unified log's SQL.

Ash calculation that exposes :tone (atom or nil) on a resource carrying an action name — the content-change colour the activity timeline tints a create/update/destroy row by, parallel to MishkaCmsCore.Activity.Calculations.Severity. Delegates to MishkaCmsCore.Activity.Helpers.tone_for/1, so a create reads :created, an update :updated, a destroy :destroyed, and anything else nil — a row the timeline then falls back to colouring by :severity.

Runtime discovery of the resources that feed the activity log, scanning every configured Ash domain (config :mishka_cms, ash_domains) — so the log adapts to whatever extensions are installed, and to future ones, with no hardcoded resource or extension names.

Ash domain for the synthetic, read-only MishkaCmsCore.Activity.ActivityEntry resource that backs the gervaz activity-log table. Not registered in :ash_domains (no migrations / DB layer) — it exists only so the resource has a domain for Ash.read.

Shared helpers used by both the PaperTrail adapter's live-broadcast path and the version-resource calculations.

DB-side merge for the unified activity log. Builds one UNION ALL across every discovered AshPaperTrail version table, AshEvents event-log table, and the oban_jobs table, then lets Postgres do the filtering, ordering and pagination in a single round-trip — instead of fetching a wide window from each source and merging in memory.

Activity source for the visual editor's own record — every change somebody made to a page, layout, component or error page in the page builder, tagged badge_label: "EDITOR".

Activity source backed by AshEvents.EventLog resources (a centralized, durable event log per extension, e.g. MishkaDocument.EventLog). Auto-discovers every event-log resource across all configured domains via MishkaCmsCore.Activity.Discovery.event_logs/0 — no per-extension wiring.

Activity source backed by Oban job telemetry. Attaches once on app boot via attach/0 (called from MishkaCmsCore.Application.start/2).

Activity source backed by AshPaperTrail-versioned resources in the MishkaCmsCore.Runtime domain. Auto-discovers any resource that ships with AshPaperTrail.Resource — no per-resource configuration.

Activity source for on-demand (lazy) runtime module compiles.

Activity source for ALL runtime drafts — PageDraft, LayoutDraft, ErrorPageDraft — each tagged badge_label: "DRAFT" with a resource label of "Page Draft" / "Layout Draft" / "Error Page Draft" so a viewer can see it came from a draft.

What an element can be made to do when it is clicked — one declared thing, so a package can add one.

Toggle, add or remove a class — the three actions that compose with the element's own CSS.

Show, hide or toggle — and the component being aimed at decides what those mean.

Show a message — core's one server action, and the shape a package's should copy.

The list or map an attribute is holding, read out of a document and written back into one.

A Phoenix.LiveView.JS command as a term, printed to source and read back from it.

The one validated shape every page-builder registry entry takes — an inspector section, a right-rail tab, a Studio tab, a canvas toolbar action, a command-palette command, an audit check, a marketplace source, an AI backend.

The page builder's plugin surface — every section of the editor is a MishkaInstaller hook, and the core ships as the seeded first participant of its own events (ADR-14).

What part of a form a component plays, declared by whoever wrote it rather than guessed here.

What a component says it should look like the moment it is dropped — declared by whoever wrote it, rather than guessed by this CMS.

One sample <:slot> entry in a component's declared furnishing.

Quoted Elixir back into the term it stands for, when it stands for a term and nothing else.

A component in the builder's palette — from the database, or contributed by an extension.

The captcha a site's sign-in, registration, forgot-password and sign-in-link forms ask, through the provider the site picked.

A captcha provider: one module that is a mishka_installer plugin on the "captcha:providers" event and implements the callbacks below. MishkaCmsCore.Captcha never names a provider: a scope picks one on the settings page's Security tab (security.captcha.provider), and its sign-in, registration, forgot-password and sign-in-link forms ask it.

hCaptcha as a captcha provider: a box the visitor ticks, which may ask a challenge.

Google reCAPTCHA as a captcha provider, in either of its two kinds — a key is made for one of them in Google's admin console

<.captcha> — the captcha a form asks, drawn where the form puts it. Every runtime template can call it (MishkaCmsCore.Runtime.LiveViewHelpers.imports/1); the sign-in pages put it above their button

Provides core UI components.

Admin dashboard widgets — the whole concern in one module: the validated descriptor every widget declares, collecting widgets (core + plugins), filtering them by the viewer's permission, arranging them by a per-user layout, and rendering one as a card.

Registers every CORE dashboard widget's own settings with the Settings page through the settings:register mishka_installer hook. Core widgets belong to no extension, so their settings land in the General tab (group Dashboard widgets) — the same place the rest of the CMS's own settings live.

Dashboard widget (a dashboard:widgets plugin): the operational pulse of the CMS. Reports the public site's online state (cms.offline, read by Settings.offline_by/1: a tenant sees whether its site is offline by its own switch or by the Global one, a master sees the Global switch and how many sites are offline by their own), the week's compile throughput as a weekday mini bar chart with a week-over-week trend (Oban.Job insert history), how many compile jobs are pending, how many active sites are missing a CSS/JS bundle (AssetsState), and the runtime-job health split (running/retrying/failed) — subsuming the standalone runtime-health widget. A master watches every site; a tenant admin only their own site's jobs and bundle. Ships default_hidden — opt-in via the widget library.

Dashboard widget (a dashboard:widgets plugin): the full-width dark KPI band at the top of the board — Sites, Users, Drafts, Open issues, Bloggers, Versions. Anyone holding anything on the dashboard sees the band; a tile shows its count only to whoever may see the card it comes from, and — to anyone else.

Dashboard widget (a dashboard:widgets plugin): shortcut links into the runtime admin pages (pages, components, layouts, sites). Static — no data query. Anyone holding anything on the dashboard sees the card, and each link only when they hold its area.

Dashboard widget (a dashboard:widgets plugin): the latest entries from the unified activity log (MishkaCmsCore.Activity.Query — AshPaperTrail + AshEvents + Oban + runtime drafts), scoped to the viewer's sites and to the areas it holds (MishkaCmsCore.Activity.Helpers.resources_for/1), rendered as a left-rail timeline with each entry's site name resolved.

Dashboard widget (a dashboard:widgets plugin): Oban job health — how many compile/runtime jobs are running, retrying, or failed. Counts the Oban.Job table via Oban.Repo.one(Oban.config(), q) (Oban has no read API). A master counts every job; a tenant admin only jobs carrying their site in args.data.site_id (how the runtime workers address a site).

Dashboard widget (a dashboard:widgets plugin): a master sees the fleet — the site total with an active/inactive split, a per-mode breakdown (API Only / Phoenix Only / Hybrid) and the average compiled bundle size (gzip CSS + JS); a tenant admin sees the same identity card scoped to their own site. Site counts come from MishkaCmsCore.Runtime.States.SiteState (ETS) and bundle sizes from MishkaCmsCore.Runtime.States.AssetsState (ETS).

Dashboard widget (a dashboard:widgets plugin): the users & access snapshot — the total user count with a Tenant/Master split, the permission catalogue (defined vs never-assigned) and how many users actually carry grants (with/without, plus the admins: holders of *, admin:* or anything under a surface, a single area included). It is the user area's card, admin:dashboard:user. Everything is counted through the MishkaCmsCore.Accounts.User and MishkaCmsCore.Accounts.Permission resources' tenancy-bypassing reads via Ash.count!/2. A master counts every user; a tenant admin only their own site's users.

Generator for MishkaCmsCore resources.

Fills a page's editing history with every state the timeline can draw, so the whole sheet can be looked at without waiting months for the awkward ones to occur naturally.

The dev showcase: what localhost answers on top of the starter site every seeded site gets.

The files the dev seeds put in a site's media library, and where any other seed gets a picture.

Dev and stress seeds for MishkaCmsCore resources.

What site-a answers on top of the starter site: a small company, with a showcase page (/:lang/showcase, linked from the header; / is the starter's own home, as on every dev site), a services list, one page per service and a team page built out of the site's own media library.

What site-b answers on top of the starter site: a knowledge base introduced by a showcase page (/:lang/showcase, linked from the header; / is the starter's own home, as on every dev site), and the three layout cases the other two sites do not show — a page on a GLOBAL layout, a page with NO layout at all, and a lang capture that is not the first segment.

The pages every dev site has beside its home: the about, contact and privacy pages a first install used to be given and no longer is.

Disposable dev fixture for verifying the Site permanent-delete teardown (MishkaCmsCore.Runtime.Workers.SiteTeardownWorker).

Shared LiveView module for extensions.

Behaviour and macro for extensions to define their routes.

A module providing Internationalization with a gettext-based API for extensions.

Runtime helpers for extensions.

First-run install state, kept in :persistent_term so the per-request install gate is a lock-free RAM read — never a per-request database count.

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.

Which extensions can install themselves onto a site, and the one call that asks one to.

The site a first install answers with: a theme, the logo, a header, a footer, a layout, a home page and an error page, written through the ordinary create actions.

Pure view of a mishka.ui_kit.bundle.v3 for the installer's component picker: groups the components by type, keeps only the light fields the picker needs (name / label / deps — never the heavy body/template/examples), and computes the transitive COMPONENT-dependency closure of a selection so required components can be locked.

Runs one function in a separate operating-system process: a peer BEAM started for the call (:peer, in OTP's stdlib) and stopped after it.

The colour a language's badge wears on an admin card.

How an admin list of translatable records shows one row per document rather than one per translation.

A translated table's bulk actions over the archive, its masters handed over first.

Layouts shared by extension LiveViews.

on_mount hook that records, for the admin chrome in MishkaCmsCore.Layouts: the current request path as @current_path (so the sidebar highlights the active item — updates on live navigation too), @notification_count (failed/retrying runtime jobs, shown as the topbar bell badge), and @current_user with its :permissions loaded. Attached to every admin page via MishkaCmsCore.ExtensionLiveView.

Helpers for authenticating users in LiveViews.

Dates, times, "how long ago", numbers, lists and language names written the way the reader's language writes them — its month and weekday names, digits, separators, word order and plural forms — from Unicode CLDR, through Localize. Nothing here is translated by hand.

Sending email, for every site through its own provider.

One email the CMS sent, or tried to (cms_mail_deliveries): who it was for, what it was for, which provider took it and how that went. Never the body: a password-reset or magic-link email carries a sign-in token.

An email provider: one module that is a mishka_installer plugin on the "mail:providers" event and implements the callbacks below. MishkaCmsCore.Mail never names a provider; a site picks one on the settings page (mailer.provider) and every email of that site goes through it.

The development mailbox as a provider: email goes to MishkaCmsCore.Mailer's own adapter — Swoosh.Adapters.Local, read at /dev/mailbox, in development, and Swoosh.Adapters.Test in the test suite — and never leaves the machine.

Resend as an email provider, through Swoosh's own Swoosh.Adapters.Resend.

Sends one queued email (see MishkaCmsCore.Mail), on the mail queue.

The notification center domain — one Ash resource, per-user real-time, pluggable through mishka_installer events, and the single public API extensions learn.

Turns one deliver/3 call into many per-recipient rows, fast and at scale.

on_mount hook that makes the topbar bell live for the current user — with zero per-page wiring.

A single, per-recipient, read/unread notification row (cms_notifications).

A plain mishka_installer plugin: when a user's permissions are replaced (cms_core:user:after_assign_permissions), notify that site's holders of the permission area (admin:dashboard:permission) AND every master holding it — a security-relevant change they should see. Registered in MishkaCmsCore.Application.

Fans a large / unbounded notification audience out to per-recipient rows off the request path.

Nightly retention sweep (Oban Cron). One bulk :prune across all sites keeps the table small no matter how many notifications are produced — read rows are dropped after notify.retention_read_days and everything after the hard cap notify.retention_max_days (both admin-editable Settings).

What a plain-text field stores: the text a person typed, with every tag taken out and nothing escaped.

Keeps every stored plugin registration on the event its module declares.

Providers as mishka_installer plugins — the half MishkaCmsCore.Mail.Provider and MishkaCmsCore.Captcha.Provider share.

PubSub name module for the MishkaCMS system.

The repo this library uses when it is developed and tested on its own.

MishkaCms Runtime Domain - Core domain for CMS runtime resources and operations.

The default base that compiled runtime modules are built on — pages, layouts, components and error pages alike.

Keeps the runtime modules the CMS builds — pages, layouts, error pages and components — as .beam files on disk, so a node that restarts, or is redeployed with the same code, loads a module it built before instead of compiling it again.

Runtime compiler for Phoenix Components with dynamic content compilation.

Runtime compiler for generating component import macro modules.

Which templates a site can actually reach, and therefore which classes its stylesheet must carry.

Runtime compiler for Error Page modules with dynamic content compilation.

Production-ready JavaScript compiler for runtime JS generation.

Runtime compiler for Phoenix Layout modules with dynamic content compilation.

Runtime compiler for Phoenix LiveView modules with dynamic content compilation.

Runtime module compiler for dynamically compiling and loading Elixir modules from AST.

The npm packages a JS hook imports, fetched once into the folder esbuild resolves bare imports from.

The pieces the four kind compilers (component, layout, page, error page) genuinely share.

A site's sitemap, built ahead of the request with Sitemapper and kept in MishkaCmsCore.Runtime.States.AssetsState under {site.name, "sitemap"} as %{index: index, files: files}. MishkaCmsCoreWeb.SeoController serves it and never builds it.

Production-ready Tailwind CSS v4 compiler for runtime CSS generation with multi-site support.

Whether anything a page or a layout draws can read the signed-in user — the question that decides, once at compile time, whether serving that page has to load and verify one at all.

Manages reusable UI components in the MishkaCMS runtime system.

Where a component a [[component …]] marker names, in stored content, is drawn as a small box when it raises while it draws, and the rest of the page draws around it.

A new component made from an existing one — the inspector modal's Duplicate.

Builds the components a page or a layout is about to draw, several at once, before it draws them.

Ready-made blocks the content editor offers beside its single components — whole MishkaCmsCore.Runtime.ContentComponents markers, components inside components and slots filled in, so an author places one and only changes the words.

Components inside content: a marker an author writes into stored HTML — a post's body — drawn as the runtime component it names. WordPress calls these shortcodes; Joomla, content plugins.

The stylesheet the content editor draws its previews with: the site's own, and the classes of every component its palette offers.

What a content editor preview is drawn through, as an HtmlSanitizeEx scrubber: every element and attribute a component draws — a button, a form, an svg, a figure, a dl — less what runs or reaches out.

The Content-Security-Policy nonce, carried from the plug into a LiveView and into every template it renders.

Deletes runtime drafts that were discarded while these resources still soft-deleted.

Temporary, isolated CSS and compiled module for a runtime preview — a draft (Page/Layout/ErrorPage) or a live original reached by its preview_token — keyed by that token in AssetsState (ETS), NEVER the live site bundle or the live page module.

The one path a site's error pages are served from, and the row sitting at it.

Manages custom error page configurations in the MishkaCMS runtime system.

An unpublished working copy of a runtime MishkaCmsCore.Runtime.ErrorPage — the ErrorPage analogue of MishkaCmsCore.Runtime.PageDraft.

What one entry of a site's Error Page answers for: an HTTP status code (404, 500, …) or :offline, the page an offline site shows its visitors.

Lets an extension ship site-facing components as a bundle, the same way the Chelekom UI kit does (§22, Path A).

Gives a site everything an extension ships and it is missing: the permission names, the components, the sample pages, the snippet. One implementation, driven by a manifest/0 the extension declares.

What a file is, read from its first bytes instead of from the name or the type its sender gave it.

Application boot handler for MishkaCms runtime system initialization and asset compilation.

Per-node concurrency limiter for runtime module compilation.

Dynamic module compiler handler for MishkaCms runtime components with concurrent compilation management.

Central rebuild coordinator for the runtime pipeline.

Shared per-site purge of runtime artifacts — compiled modules, cached CSS/JS, routing entries, and on-disk templates. Used by both the permanent-delete teardown (MishkaCmsCore.Runtime.Workers.SiteTeardownWorker) and the activate/deactivate/rename worker (MishkaCmsCore.Runtime.Workers.SiteRuntimeWorker).

Trivial loader for mishka.ui_kit.bundle.v3 JSON bundles.

What a site's kit components are against the bundle on disk now, and the one write that brings them forward without taking an admin's edits with it.

Manages JavaScript and TypeScript hooks in the MishkaCMS runtime system.

The languages a site publishes in, and what the runtime makes of a path segment that names one.

Manages layout templates in the MishkaCMS runtime system.

An unpublished working copy of a runtime MishkaCmsCore.Runtime.Layout — the Layout analogue of MishkaCmsCore.Runtime.PageDraft.

The render-time bridge between a compiled runtime template and a compiled runtime component.

Manages media files (images, videos, documents) in the MishkaCMS runtime system with full versioning support.

Manages media categories in the MishkaCMS runtime system with full versioning support.

A plain mishka_installer plugin: after a media file is saved (cms_core:media:after_save — replaced in place, archived), what is kept of its site's icons is dropped when the site's favicon or the picture of its icons is that file, and the next request for one reads it again (MishkaCmsCore.Runtime.SiteIcon.forget_file/2). Registered in MishkaCmsCore.Application.

Validates origins against sites stored in ETS for WebSocket connections and CORS, and reads the host or the address out of what a request or a setting carries (host_name/1, web_url?/1, site_path?/1) with URI.parse/1 rather than by cutting strings.

Manages web pages in the MishkaCMS runtime system with full versioning support.

The seam the Assistant rail talks to, and an honest default that does not pretend to be an AI.

The default assistant: it restates the request, names the scope it would act on, and says plainly that no model is configured.

The v2 prototype's Data tab: repeating a node over a CMS collection.

Whole sections an author drops in one click — a hero, a feature row, a pricing table.

A policy check: the actor may open the builder the row's own section names — its runtime area or admin:dashboard:code (MishkaCmsCore.Runtime.PageBuilder.Section.permissions/1).

What a palette card looks like before you drop it — the small document shown when the pointer rests on a card.

Drops cached palette-card previews when the component they drew has been recompiled.

Named styles a site can reuse — style once, apply to twenty elements, change it in one place.

Whether this deployment allows code written by hand, and what a refusal says.

How much of the stored code in this database carries a signature this deployment cannot verify.

Compiles a page's code without loading it, so an author is told about a mistake instead of a visitor being shown one.

One short warning for a snippet that calls something worth reading twice.

What a snippet reads, what it calls and what it assigns, read off the parsed code.

An HMAC over each stored snippet, so code that did not come through this application does not run.

The shape and the wording of a review note — what the storage layer is built around.

When an element is on the page at all, without anyone writing Elixir.

The one authority on "may this node go inside that one" — the HTML content model plus the component/slot rules, in Elixir, on the server.

Sample content for the editor's Assistant thread and its audit list, so those panels can be judged before there is anything real in them.

The Studio's Design tokens tab, as a Gervaz table.

The editor's autosave: the working document written to a draft row, not the live one.

One open editing session: the working tree plus everything around it — selection, hover, undo history, clipboard, per-node locks, the breakpoint and state being edited.

What happened to the document, in the words a person would use, and the stack that lets them go back.

CSS an author writes by hand for one element.

What a document's minted element classes have to become when its markup lands in ANOTHER document.

The plain HTML an author reaches for constantly — a section, a column grid, a heading, a list.

The example list behind a component's Examples modal — curated out of the two sets a kit bundle ships, which are not the same thing and were never reconciled.

What part of a form a palette component plays, what part a node in the document plays, and how a field is staged for preview.

Saves a builder section through its resource's own actions.

Colour for the code the editor shows, driven by tokenizers rather than by regular expressions.

Turns the editor's settled entries into durable rows, and reads them back.

The runtime JS hooks an element can be attached to, and the two attributes that attach it.

What an icon set has to answer so the inspector can offer it.

Every icon set the inspector can offer, as one catalogue.

Icon sets that arrived INSIDE an installed kit, needing no code and no configuration.

One DOM id per element, and the references inside a snippet follow when one has to change.

Decides which control a component attribute gets in the property panel, and groups the attributes into sections — all as pure functions, so the no-code surface is testable without a browser.

What an element does when it is clicked — the registry the panel reads and a package appends to.

A component's INTERFACE — the attributes and slots it declares — as data the editor can edit and check.

Whether a layout still renders the page it wraps.

Format HEEx templates from .heex files or ~H sigils.

Everything the Studio sheet shows — design tokens, presets, the package registry and snippets — behind one behaviour, so the panel is written once and the source can change underneath it.

A MishkaCmsCore.Runtime.PageBuilder.Marketplace adapter over the registry this install actually has: the UI-kit bundles sitting on disk.

The default MishkaCmsCore.Runtime.PageBuilder.Marketplace adapter: the design prototype's exact seed data, in memory.

One design token — the validated shape both the Mock adapter and any real backend must produce.

Every Elixir expression a HEEx template contains, found by tokenising it.

Naming somebody in a note: @who in the body, and the notification that follows it.

One validated edit request — the only thing allowed to reach a MishkaCmsCore.Runtime.PageBuilder.Tree combinator.

What can be learned about a page's forms by parsing the page's own template.

The Forms group of the audit rail: what is wrong with the page's forms, and the tree edit that puts each one right.

Wraps a field dropped with no form around it, and binds a field dropped inside one.

One seed per form, and the five names derived from it.

How a blank document begins — the few shapes offered on a page with nothing on it yet.

Where a thing lands — the answer shared by a click, a drag, and a template that arrives on its own.

Who else is in this editing session — real Phoenix.Presence on the session's own PubSub topic.

What a component should look like the moment it lands on a page — this site's answer, not the kit's.

What the builder needs from the review thread: the counter, and getting to an element.

What "the template" means for each of the four things the builder edits, and how to write one back.

The SEO a page or a layout stores, as the builder's settings modal edits it: a Gervaz form.

A number that says which one of these this is, kept in step when another arrives.

What a component should SAY the moment it is placed.

What the editor refuses to let past in development, and forgives in production.

Raised when the editor finds itself in a state that should not be reachable.

The stylesheet row the page builder owns for one site, found by its reserved name: the reusable classes (MishkaCmsCore.Runtime.PageBuilder.Classes) and an element's own CSS (MishkaCmsCore.Runtime.PageBuilder.ElementCss) are each one row of Stylesheet.

The design tokens a site actually renders from — read out of, and written back into, its own stylesheet rows.

The editable model of a template: a flat, id-keyed map of MishkaCmsCore.Runtime.PageBuilder.Tree.Node structs plus the list of root ids.

One node of an editable template — one shape for everything the editor can address.

What you apply to one version of a document to get an earlier one back.

A box shadow as the five things an author is actually choosing, and back again.

Turns the inspector's recorded overrides into Tailwind utilities, and reads them back out again.

An element kept in the page but shown to nobody: hidden from visitors, the way a line of code is commented out.

Composing a component from its own option lists, before anything is placed.

One recorded change to a document in the visual editor — the durable half of the editor's history.

The document a history row can restore — gzipped HEEx, in a table of its own.

A note left on a document in the visual editor — on one element, or on the page as a whole.

An unpublished working copy of a runtime MishkaCmsCore.Runtime.Page.

A page in the other languages it is published in: what <html lang dir>, the hreflang links and a language switcher read, and what "Add translation" writes.

What a compiled page does when a visitor types in, or submits, a form its author drew.

One form's declared rules, executed as a schemaless Ecto.Changeset.

The language fields a page written before them has to be given, before anything reads them.

What a page says to a visitor when something on it is clicked.

Pages a site is GIVEN rather than written by hand — an extension provisioning itself, the dev seeds — and the one question every one of those callers has to ask before it writes a row: is this page already somebody's?

What a site's sample pages are against the ones this checkout ships, and the one write that offers the newer ones without taking an admin's page away from them.

A plain mishka_installer plugin: after a page of a site is saved (cms_core:page:after_save — created, updated, published, a master's twin alike), its share card is looked at in the background and drawn when what it shows changed (MishkaCmsCore.Runtime.ShareCard.refresh/3), so it is ready before the first visitor. A save that leaves the card's title, description and picture alone draws nothing. A page that shows records is left to the plugins of those records' own after-save events. Registered in MishkaCmsCore.Application.

A plain mishka_installer plugin: answers MishkaCmsCore.Runtime.ShareCard.targets_event/0 with the core's own share cards on the site asked — each active page that shows no record, as {:page, id}, under "mishka_cms_core". A page that shows records is left to the producer of those records. The pages are streamed by id, as the system reads them for a share card (MishkaCmsCore.Runtime.ShareCard.specs/3). Registered in MishkaCmsCore.Application.

The rows a page shows, read once for the request and handed to everything that draws them: the head (MishkaCmsCore.Runtime.Seo), the frame (MishkaCmsCore.Runtime.SiteLayout) and the page's own blocks.

A database page's starting state: the assigns it declares in Page.state, put on the socket by the LiveView that serves the page.

Live file uploads on a published page, from a declaration an author writes rather than Elixir.

Names the admin surface a pipeline's requests come from, so that a policy listing both surfaces' names counts only this surface's: permissions: ["admin:dashboard:runtime:page", "admin:api:runtime:page"] on MishkaCmsCore.Accounts.Checks.AccessPermissions takes admin:api:runtime:page on the API and admin:dashboard:runtime:page on the dashboard.

Closes a site's API while the site is offline, to everyone but its own API admins and the masters (users whose site_id is nil) who are API admins: who holds anything under admin:api (MishkaCmsCore.Accounts.Helper.master_admin?/2 and site_admin?/3 on the :api surface). A dashboard-only admin reaches the site's screens, not its API, and is refused like anyone.

CORS (Cross-Origin Resource Sharing) plug for API endpoints.

Multi-tenant API plug for domain-based tenant resolution and context management.

The JSON:API side of MishkaCmsCore.Accounts.AuthRateLimit: hands every action the address conn.remote_ip names (the endpoint's plug RemoteIp has read it) as the ash_authentication_request context, marked api?: true so the actions ask no captcha (MishkaCmsCore.Accounts.CaptchaCheck), and gives a 429 AshJsonApi wrote for a refused sign-in or request its headers.

Answers every request in the language its address names, sends one whose address is not the site's form of it to the one that is, and remembers which language the visitor is reading.

The bytes a webhook was sent, kept for its signature check.

Site lookup plug for multi-site applications with host-based site resolution.

Makes a hook named for one site work while another site's draft is being previewed.

One address of a site answered with a redirect to another — a page or a file that moved, and the visitors, search engines and shared links that still ask for its old address.

The redirect rules of each site, answered on the request that asks for an old address.

The body of a versioned resource's :restore_version_api (a site's own call) and :master_restore_version (a master's) generic actions: read the record, refuse what may not be restored, check that the version is one of THIS record's, and run the resource's :restore_version update with the record's own site as the tenant.

A centralized calculation for generating runtime module names across different resources.

A reusable calculation that exposes a plugin-transformable rendered version of a content field — the Ash-idiomatic equivalent of the WordPress the_content / Joomla onContentPrepare filter.

What a record said in one of its own versions, for a preview before a restore.

Lets a document be created without any markup, by giving a blank one the empty starter container.

Writes an error page's path from the site it belongs to, on every action that can set one.

Names a page after its title when the writer did not name it.

Tells the people a note names that it names them.

A reusable before_action change that lets a plugin transform or reject an action before it runs (the WordPress wp_insert_post_data / pre_comment_approved, Joomla onContentBeforeSave seam).

Refuses to restore a translation unless its master is live once the restore is done.

Processes uploaded file from consumed uploads and sets derived media attributes.

After a :publish / :master_publish of a MishkaCmsCore.Runtime.ErrorPageDraft, writes the draft's fields (including the whole error_pages collection) to the live MishkaCmsCore.Runtime.ErrorPage — creating it when the draft has no source_error_page_id, otherwise updating it.

After a :publish / :master_publish of a MishkaCmsCore.Runtime.LayoutDraft, writes the draft's fields to the live MishkaCmsCore.Runtime.Layout — creating the layout when the draft has no source_layout_id, otherwise updating the existing layout.

After a :publish / :master_publish of a MishkaCmsCore.Runtime.PageDraft, writes the draft's fields to the live MishkaCmsCore.Runtime.Page — creating the page when the draft has no source_page_id, otherwise updating the existing page.

Drops a record's cached preview stylesheet after the row is written.

Registers the keys a saved component, page or layout asks t/1 for — in its template and in the code of its helpers, events and infos (MishkaCmsCore.Runtime.Translations.source_keys/1) — so they appear in the admin ready to translate.

Detaches a draft's editing history from it as the draft is deleted, so the rows survive and join the live record's own timeline.

Copies a version's snapshot back onto the record it was taken of.

Owns the runtime side of a Site :create / :update / :destroy (archive): keeps SiteState in sync and recompiles or purges the site's dependent parts (Pages, Layouts, ErrorPages, Components, JsHooks, Stylesheets).

Schedules the background teardown of a site's in-memory artifacts on :permanent_destroy.

Triggers background compilation jobs for runtime resources.

Automatically sets language_group_id for master collections.

Writes the action's record argument — the kind of record each address of a page shows, "blog:post" — into extra["record"], where MishkaCmsCore.Runtime.Seo.record_of/1 reads it. An empty one takes it out: the page shows one thing at one address.

Sets the site_id attribute on Version resources when multitenancy is bypassed.

Signs each stored snippet on the way in, so the compiler can tell what this application wrote.

Writes the tenant the action runs under into the record's site_id, replacing whatever was sent.

Runs the action under the tenant of the record's site_id.

Whether the actor holds what writing a setting takes: the :permission its definition declares, or admin:dashboard:setting when it declares none or no definition names the key (MishkaCmsCore.Settings.write_permission/1). Asked of the row's key, within the actor's own site as every MishkaCmsCore.Accounts.Checks.AccessPermissions check is.

Embedded resource for helper function definitions in runtime resources.

Mixin for configuring PaperTrail version tracking on Component resources.

Mixin for configuring PaperTrail version tracking on Error resources.

Mixin for configuring PaperTrail version tracking on JsHook resources.

Mixin for configuring PaperTrail version tracking on Layout resources.

Mixin for configuring PaperTrail version tracking on MediaCategory resources.

Mixin for configuring PaperTrail version tracking on Media resources.

Mixin for configuring PaperTrail version tracking on Page resources.

PaperTrail version-tracking config for the Stylesheet resource.

Who reads a runtime resource's versions — the policies of the version resources AshPaperTrail generates.

Keeps a written page's row in MishkaCmsCore.Runtime.States.SiteState's editions index, on the node that wrote it — what MishkaCmsCore.Runtime.PageEditions.editions/2 reads instead of the database.

A reusable Ash notifier that emits a MishkaInstaller plugin event after every action (create / update / destroy, including custom-named actions) on any resource it is added to.

A reusable Ash notifier that rebuilds a site's sitemap after a write to a row it is built from.

A reusable read Preparation that lets a plugin adjust the query before a read runs — the WordPress pre_get_posts / posts_where, Joomla onContentSearch seam.

Scopes a read to one site plus the shared rows, deciding whose site from WHO IS ASKING.

Refuses a write by a caller that does not act on the site the record belongs to.

A page and an error page are never global; a layout and a component can be.

Refuses code written by hand when this deployment has turned it off, and Elixir from a write that names no actor.

Validates domain formats for Site resources including host, frontend_domain, and allowed_origins.

Refuses to archive or delete a media folder that still holds a file or another folder.

Refuses a media folder put inside itself, or inside one of its own subfolders.

Stops a tenant changing a row that belongs to no site.

A JS hook's npm packages and modules are what the JavaScript compiler fetches and writes to disk, so a hook is refused unless each package is one MishkaCmsCore.Runtime.Compilers.NpmPackages can fetch, and each module a plain name.js with its content — written beside the hook, and nowhere else.

Refuses a component whose declared attributes or slots could not compile.

Keeps a master site that masters can sign in on.

Refuses a page whose code will not compile, at the action boundary, before it is stored.

Refuses a page whose state names an assign it may not have.

Refuses a page whose uploads declares a file field no visitor could use.

The two things a reply's parent has to be.

Validates a resource path as a safe, canonical URL route path — used by the Page, Layout, and ErrorPage resources and their drafts.

Refuses to destroy the original edition of a translated record while translations of it are live.

Refuses a page whose public addresses cannot be what it says, or are already another page's.

Refuses a record that names, by id, a record of another site.

Refuses a global row about a document that cannot itself be global.

Refuses a page that places two live components under one id.

Refuses markup that places a live component somewhere a live component cannot live.

Refuses a component that says it keeps state and is shaped so that it cannot.

Refuses a layout, component or error page whose markup HEEx will not take, at the action boundary.

Holds an action to the kind of caller it was written for: a master, or a site.

Refuses a write that names a record of another site.

Refuses a site's change to a row that belongs to no site, or to another one.

Builds the sandboxed-iframe document and the Overview details for the runtime preview modal (MishkaCmsCoreWeb.Live.Admin.Components.RuntimePreviewModal), for any module-backed runtime resource.

Read-only runtime status of a single runtime record (Component, Layout, ErrorPage, Page, Stylesheet, JsHook): its latest Oban compile job and whether its compiled artifact is present — a module loaded in RAM (module-backed resources) or the site's combined CSS/JS bundle (asset-backed Stylesheet/JsHook).

What a database page puts in <head> for search engines and link previews, the tags an editor can offer by name, and the variables a stored SEO value may use.

Durable key/value CMS settings, multitenant like MishkaCmsCore.Runtime.Layout (global? true): a row with site_id = nil is a GLOBAL setting (one value for every site, master-managed); a row with a site_id is a PER-SITE setting owned by that tenant (site title, SEO, …). Both live in the one cms_settings table, keyed by [:key, :site_id].

A plain mishka_installer plugin: after a setting every core share card shows is saved (cms_core:setting:after_save, MishkaCmsCore.Runtime.ShareCard.site_settings/0 — the site's name and logo), every share card of its site is looked at again in the background (MishkaCmsCore.Runtime.ShareCard.redraw_all/2), of every active site for a global value. Only a card whose data changed is drawn. Registered in MishkaCmsCore.Application.

A plain mishka_installer plugin: after the site.app_icon or the site.favicon setting is saved (cms_core:setting:after_save, MishkaCmsCore.Runtime.SiteIcon.settings/0), what is kept of its site's icons is dropped — of every site for a global value — and the next request for one draws them from the new picture (MishkaCmsCore.Runtime.SiteIcon.forget/1). Registered in MishkaCmsCore.Application.

The picture a link to a page shows where it is shared — X, Telegram, WhatsApp, LinkedIn, Slack, Discord — drawn from what the page is: 1200×630 JPEG, the size every one of them shows large.

The Oban queue share cards are drawn on, :share_cards, and how many are drawn at once: the share_cards.workers setting (Settings → SEO → Share cards, master only), from 0 to 4, 1 by default. 0 pauses the queue: the cards wait in it, and are drawn once it is set above 0 again.

Manages sites in the MishkaCMS multi-tenant runtime system.

Whether a site is up, and who may reach it when it is not.

Read-only health roll-up for a single Site, for the admin Sites table's expanded row. A Site is an aggregate (it owns Pages/Layouts/Components/ErrorPages/Stylesheets/ JsHooks + its CSS/JS bundles + a serving state), so this is a dashboard, not the linear pipeline used per-record by MishkaCmsCore.Runtime.RuntimeStatus.

A site's icons and its web manifest, each its own, answered on the site's own address (MishkaCmsCoreWeb.SeoController)

The one layout every page of a site is drawn in, site-main, and the contract through which a page, or the extension that owns it, hands that layout what only the page knows: the languages it is written in, and the bar under the header.

A plain mishka_installer plugin: after a site is edited (cms_core:site:after_update — its address or its name), every share card of it is looked at again in the background (MishkaCmsCore.Runtime.ShareCard.redraw_all/2), and a card that shows the old address is drawn with the new one before anybody visits the page. The saves of a burst are one look. Registered in MishkaCmsCore.Application.

A named piece of markup an author saved out of the visual editor to reuse somewhere else.

High-performance ETS-based state manager for multi-site asset storage and retrieval.

O(1) ETS cache of rendered palette-card previews, keyed by the template a card would drop.

O(1) ETS record of runtime modules that failed to compile, keyed by module atom.

What the undo history holds, kept beside the session rather than inside it.

The ETS table behind the page builder's live editing sessions, keyed by session token.

Hammer's ETS table that signing in, the password reset request and the magic-link request are counted in: one row per key, whose window opens at the key's first hit and closes per milliseconds later. Start it before anything that can run those actions. See MishkaCmsCore.Accounts.AuthRateLimit.

ETS copy of the active MishkaCmsCore.Runtime.Redirect rules, so a request is answered without a query.

ETS-backed cache of the CMS settings, loaded once at boot and refreshed on change so reads are lock-free on every request (mirrors MishkaCmsCore.Runtime.States.SiteState). The durable source of truth is MishkaCmsCore.Runtime.Setting; application code should go through the MishkaCmsCore.Settings facade rather than this module directly.

High-performance ETS-based state manager for multi-site configuration and lookup.

ETS cache of who a signed-in user is to the site gates: their site, their address and the names of the permissions they hold, and whether they are a dashboard admin and an API admin (MishkaCmsCore.Accounts.Helper.dashboard_admin?/1 and api_admin?/1). A request that has not loaded its user — a file, a live navigation — is answered from here by the user's id, with no query, and every Helper admin predicate and MishkaCmsCore.Accounts.AuthGate.signs_out?/2 answer a cached viewer exactly as they answer the user.

High-performance ETS-based state manager for web page metadata with multi-index support.

MishkaCmsCore.Runtime.LiveViewHelpers for the surfaces that render a template to a STRING.

Manages CSS stylesheets in the MishkaCMS runtime system.

A word a runtime template asks for with t/1, in one language, for one site.

A plain mishka_installer plugin: after a translation of a site's name is saved (cms_core:translation:after_save — a row whose key is the site.title setting, MishkaCmsCore.Runtime.Seo.site_name/2), every share card of that site is looked at again in the background (MishkaCmsCore.Runtime.ShareCard.redraw_all/2); for a global row, of every active site named so. Only a card whose data changed is drawn. Registered in MishkaCmsCore.Application.

The words a runtime template asks for with t/1, and where they come from.

Collects every share card of a site and queues a look at each: the planner MishkaCmsCore.Runtime.ShareCard.redraw_all/2 enqueues, on :share_cards.

Looks at the share card a page or a record shows now (MishkaCmsCore.Runtime.ShareCard.look/3) and queues its drawing when it is not drawn yet — or, with force, draws it again over the old file — and keeps each card file its pages show from the next sweep. Enqueued by MishkaCmsCore.Runtime.ShareCard.refresh/3, on :share_cards, one job per site and target at a time: the saves of a burst are one look.

Deletes the share cards of a site that no page shows any more: the planner (MishkaCmsCore.Runtime.Workers.ShareCardPlanWorker) queues one, on :share_cards, after it has queued a look at every card of the site, with the time it started as since.

Gives a site created in the admin the content a first install gets, and recompiles or purges a site's dependent parts when the site is activated / deactivated / renamed / rehosted / archived / unarchived. Enqueued by MishkaCmsCore.Runtime.Resources.Changes.ScheduleSiteRuntime on the dedicated :maintenance queue, deduped per site_id.

Frees everything a permanently-deleted site loaded into memory.

CMS settings — one module for the whole subject: reading and writing values, plus the registry of declared settings the admin page renders.

The admin sidebar as data — collected from plugins at runtime, filtered by what the viewer may see, and ordered by what the admin arranged.

A collapsible section of the admin sidebar — the heading a set of MishkaCmsCore.Sidebar.Item links sit under, and the unit an admin collapses, reorders or hides.

One link in the admin sidebar.

Registers every sidebar entry's position and on/off switch with the Settings page, through the "settings:register" mishka_installer hook — the same plugin shape MishkaCmsCore.Dashboard.WidgetSettings uses for widgets.

on_mount hook that refuses an admin page to anyone whose permissions its declaration does not allow. Attached once, in MishkaCmsCore.ExtensionLiveView, so it covers every admin LiveView without a line in any of them.

What a component in a mishka.ui_kit.bundle.v3 file has to look like before this CMS will install it.

Keeps the language an admin reads the dashboard in, chosen in the topbar's user menu.

High-performance asset delivery controller for CSS and JavaScript resources.

Authentication controller for MishkaCms user authentication workflows.

UI overrides for the AshAuthentication.Phoenix components the sign-in pages place — the token pages (Reset, Confirm, MagicLink.SignIn). They render inside the pages' card, so these overrides give each component's field/input/button/error the same Tailwind utility classes the hand-written forms use. Pass them as overrides={[MishkaCmsCoreWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default]}.

The submit handler for a component's form — the inspector's Configuration tab and the builder's Interface tab are both this form.

The submit handler for the Studio's "Save as snippet" box.

The page builder's chrome: top bar, the two side panels, the crumb bar, the code drawer and the overlays — everything around the canvas, which is its own LiveView inside an iframe (MishkaCmsCoreWeb.Live.Admin.Builder.CanvasLive).

The page-builder canvas: the document itself, rendered inside a same-origin iframe with the site's compiled CSS rather than the admin's.

Every region of the page-builder chrome as a function component: the 56px top bar, the two resizable side panels, the crumb bar, the canvas frames, the code drawer, the command palette and the overlay sheets.

Real-time activity feed driven by a pluggable list of sources, rendered as a collapsed "Logs" toggle over a System Logs panel.

The server half of the content editor MishkaCmsCoreWeb.UIAdapters.Admin.textarea/1 draws for a :textarea field with ui do extra %{content_editor: true} end: the palette an author places from, the settings of a placed component, and the answers the editor's hook asks for. See MishkaCmsCore.Runtime.ContentComponents for the marker and how a page draws it.

Media Manager inside the content editor's settings (MishkaCmsCoreWeb.Live.Admin.Components.ContentComponentPicker): the pictures, or the videos, of the site the record is on, newest first, 48 at a time with Load more for the next, searchable by name, and an upload that adds a file to the site's media and picks it. At the top, All media, every folder's files are listed and the folders beside them; a folder opened lists only its own, and the folders inside it, with the way back up above.

Shared chrome + display helpers for the admin record modals (the runtime preview modal and the site details modal). Keeps ONE source of truth for the modal shell, segmented tabs, and the small card helpers — each consumer supplies only its own header bits (:subtitle/:actions slots), tab list, and tab content (:inner_block).

Setting an extension up on a site: the form, the report it leaves on screen afterwards, and the three places a dashboard puts them.

Where a site stands against the version of an extension it is running, read and drawn.

A form's picture field: the address the record stores, a preview of it, and the site's media library under it — an upload dropped there, or a file chosen from it, fills the field. It is what MishkaCmsCoreWeb.UIAdapters.Admin.text_input/1 draws for ui do extra %{media: :images} end, with folder: "name" naming the library folder its uploads are filed in.

Moves a file or a folder of the media library into another folder of its site, chosen by browsing the folders the way the library's own rail does.

Permission record modal: a read-only view (name, description, created/updated) that flips to the editable MishkaGervaz.Form.Web.Live permission form via Edit Permission (master users only). Also hosts the create form when opened with no record (the "+ Add Permission" flow).

The signed-in account's recovery codes, a security card on the settings page's Preferences tab (SecurityCards): one-time codes for signing in when its authenticator app is lost. Made with a current code from the app (generate_recovery_codes), shown once and replaced by the next ones; otherwise the card says how many are left. An account without an authenticator is told to set one up first. See MishkaCmsCore.Accounts.SecondFactor.

Tabbed inspector modal for a single module-backed runtime record (Page / Component / Layout / ErrorPage), opened from a table's "Preview" row action.

Renders a record's runtime status from a MishkaCmsCore.Runtime.RuntimeStatus.compute/2 map: a four-step pipeline (Saved → Active → Compiled/Bundled/Lazy → Live) where each step is :done (solid green), :current (light green), or :future (gray, not yet reached), plus a single "Currently:" box that carries the live state + message and is the only element that changes color by state (green/amber/red/blue/gray).

The cards of an account's own sign-in security, drawn on the settings page's Preferences tab: the core's — its authenticator app (TwoFactor) and its recovery codes (RecoveryCodes) — and any an extension adds on the "account:security:cards" mishka_installer event, as a widget is added on "dashboard:widgets".

The share card a page or a record shows where a link to it is shared (MishkaCmsCore.Runtime.ShareCard), in its edit modal: the card as drawn, and Regenerate, which draws it again over the old file (ShareCard.refresh(site_id, target, force: true)) and shows the new one once it is written.

A Redraw share cards button: after a confirmation, every share card of a site — or of every active site — is drawn again over its old file in the background (MishkaCmsCore.Runtime.ShareCard.redraw_all/2 with force: true), one at a time on the :share_cards queue, and the button says how many cards were queued once the planner has collected them.

Site record modal: Overview (read-only Site Details + real MishkaCmsCore.Runtime.SiteHealth metrics + Allowed Origins) and Configuration (the live MishkaGervaz.Form.Web.Live Site form).

Renders a site's runtime health from MishkaCmsCore.Runtime.SiteHealth.compute/1: a one-glance headline (Healthy / Recovered / Working / Needs attention / Offline / Inactive), the site's in-progress and failed compile tasks, a per-resource active/inactive/ archived roll-up, CSS/JS bundle status + freshness, and a compact recent-activity list.

One word in every language — the body of the admin Translations modal and of the visual editor's Translations sheet alike. A chip per language with its translation, or "Missing", beside it, and Gervaz's MishkaCmsCore.Runtime.Translation form for the one picked.

The alert an admin sees when archiving, or deleting for good, an original edition is refused because its translations are still live — with a button that archives (or deletes for good) those translations at once, after a confirmation naming them.

The signed-in account's authenticator app, a security card on the settings page's Preferences tab (SecurityCards): set up, or turned off. See MishkaCmsCore.Accounts.SecondFactor for what it changes at sign-in. Turning it on or off tells the other cards (:account_security_changed), so the recovery codes card follows.

User record modal: a read-only view of a user (id, email, site, confirmation status, permissions) that flips to the editable MishkaGervaz.Form.Web.Live user form via the Edit User button, and hosts the create form when opened with no record (the "+ Add User" flow).

Admin activity-log page at /admin/dashboard/runtime/activity. Mounts the standard MishkaGervaz.Table.Web.Live over MishkaCmsCore.Activity.ActivityEntry — a read-only resource whose manual read merges AshPaperTrail + AshEvents + Oban into one filterable, paginated timeline.

The signed-in user's notification inbox at /admin/dashboard/notifications.

The admin's component list, and the modal that makes a new one.

The admin's error-page list. Creating, viewing and editing all happen in the one inspector modal (MishkaCmsCoreWeb.Live.Admin.Dashboard.Runtime.PreviewHelpers) — the eye opens it on Overview, the pencil on its Configuration tab, and "+ Add" on an empty form. There is no separate error-page-form-modal: two mounts of one form component on a page collide on their DOM id.

The admin's layout list. Creating, viewing and editing all happen in the one inspector modal (MishkaCmsCoreWeb.Live.Admin.Dashboard.Runtime.PreviewHelpers) — the eye opens it on Overview, the pencil on its Configuration tab, and "+ Add" on an empty form. There is no separate layout-form-modal: two mounts of one form component on a page collide on their DOM id.

Media management LiveView with grid/list view and a folders sidebar. Uses MishkaGervaz MediaGallery template for media display with real-time updates. Media upload/edit form powered by MishkaGervaz Form DSL.

The admin's page list, and the modal that makes a new one.

Shared wiring for the runtime "Preview" modal across every module-backed runtime admin table (Page / Component / Layout / ErrorPage).

The admin's list of a site's redirect rules — a Gervaz table and a Gervaz form, nothing drawn by hand.

Shared expanded-row wiring for every runtime admin table (Component, Layout, ErrorPage, Page, Stylesheet, JsHook). The expanded row shows the record's real runtime status (MishkaCmsCore.Runtime.RuntimeStatus) as a live timeline that refreshes while open.

The CMS settings admin page: a left rail of category tabs and a content panel whose fields are rendered dynamically from MishkaCmsCore.Settings definitions, one input per setting by its type. The markup lives in the co-located settings_live.html.heex.

Admin dashboard LiveView for runtime CSS stylesheets.

The admin's list of the words runtime templates ask t/1 for — a Gervaz table and a Gervaz form, nothing drawn by hand.

Where a sign-in goes back to, as a token this application signed.

Extension routes for MishkaCmsCore (core admin dashboard, runtime, accounts).

A site's /robots.txt, /favicon.ico, its home-screen icons and /site.webmanifest, and /sitemap.xml, with the /sitemap-00001.xml.gz… files it lists, each its own, answered on the site's own address.

A row action that copies a record and leaves the original alone — Gervaz's ActionType behaviour rather than a custom event.

A MishkaGervaz custom table template: an activity log drawn as a vertical timeline — a rail with a severity-tinted node per event, day headings across it — rather than a table.

Everything the builder's Assets sheet draws around its files, shared by both of its views.

A MishkaGervaz table template: the file grid in the builder's Assets sheet.

A MishkaGervaz table template: the file LIST in the builder's Assets sheet.

The chrome every document card page wears: the control row, the count over the list, the loading veils and the pagination underneath.

A MishkaGervaz custom table template: the list of drafts inside the page builder's Versions sheet.

The visual editor's history, drawn as a MishkaGervaz custom table template: a vertical rail with a bucket-tinted node per change, the assistant's own words where it has them, and a Revert on every row whose document was kept.

A MishkaGervaz custom table template: the inbox at /admin/dashboard/notifications, rendered as one card of full-width rows (checkbox, severity tile, title/body/time, per-row actions) rather than a table — backed by Gervaz, so filters, selection, bulk actions, pagination and realtime come from the resource DSL rather than from this module.

A MishkaGervaz custom form template: the composer at the FOOT of the builder's Review sheet.

A MishkaGervaz custom table template: the thread in the builder's Review sheet.

A MishkaGervaz custom form template, and the events its markup sends: the SEO tab of the builder's settings modal, over MishkaCmsCore.Runtime.PageBuilder.SeoSettings.

A MishkaGervaz custom form template: the "Save as snippet" box at the top of the Studio's Snippets tab.

A MishkaGervaz custom table template: the saved-snippet list inside the page builder's Studio.

A MishkaGervaz custom table template: the Design tokens list inside the page builder's Studio.

The version-history modal's body and footer, shared by every admin page that has one.

UI adapter for the admin card pages, extending the Tailwind adapter.

The UI adapter for the builder's Review panel — the editor's own look, over Gervaz's behaviour.

Refuses a request whose user holds none of the permissions a route requires.

The response headers that are the same whatever was asked for, and the Content-Security-Policy, which is not.

Writes messages marked outside the core into the core's .pot files, for mix mishka_cms_core.gettext.gervaz and mix mishka_cms_core.gettext.extensions.

Mix Tasks

Builds the two BUILD-TIME stylesheets, into the host app's priv/static.

Prints which globally-installed components a site can actually reach, and what that saves.

Re-signs the Elixir stored in pages, layouts, components and error pages.

Adds every message the extensions mark with MishkaCmsCore.Gettext to the core's .pot file of its domain.

Adds every message of MishkaGervaz's .pot files to the core's .pot file of the same domain.

Deletes runtime drafts that were discarded back when discarding was a soft delete.