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.
Signing up never makes a master.
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
Cloudflare Turnstile as a captcha provider.
<.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.
The heroicon set, as a MishkaCmsCore.Runtime.PageBuilder.IconLibrary.
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.
The Studio's snippet list, over MishkaCmsCore.Runtime.Snippet.
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.
Turns a MishkaCmsCore.Runtime.PageBuilder.Tree back into a HEEx template — the other half of the
round trip MishkaCmsCore.Runtime.PageBuilder.Tree.parse/1 opens.
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.
Mixin for configuring PaperTrail version tracking on MishkaCmsCore.Runtime.Redirect.
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 copy of MishkaCmsCore.Runtime.Translation, so t/1 is answered without a query.
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.