Not released yet. PeltyCMS is under development and not released yet; these docs describe the upcoming release and may change.
PeltyCMS Developer docs
User docsDevelopers
v0.1.0

Developer docs/Extension reference

Architecture / codebase map

Every crate and module of the Rust workspace with the summary from its //! doc, generated from source. Big crates group modules by domain folder (for example pelty-database/src/billing/, pelty-api/src/routes/finance/); old flat paths still work through pub use re-exports. A test fails when a module has no //! doc.

#pelty-api

HTTP layer: routing, extractors, error mapping. Business logic lives in service crates; SQL lives in pelty-database.

ModuleWhat it does
access/captcha.rsCaptcha protection for public forms (WHMCS "Captcha Form Protection").
access/duo.rsDuo Security two-factor (Duo Universal Prompt, OIDC auth-code flow).
access/fraud.rsOrder fraud screening: built-in rules plus pluggable FraudHooks (e.g. a MaxMind or FraudLabs integration). Scores add up (capped at 100); the settings thresholds pick accept / review / reject. A hook that errors is logged and ignored (fail-open) so an outage never blocks sales.
access/mod.rsAccess and security: OAuth login, the OIDC provider, SAML, Duo, captcha, security policy, security headers, trusted proxies and fraud checks.
access/oauth.rsSocial / SSO login (OAuth 2.0 authorization code + PKCE, OpenID Connect).
access/oidc_provider.rsPeltyCMS as an OAuth 2.0 / OpenID Connect provider ("Sign in with Pelty").
access/saml.rsSAML 2.0 Service Provider (addon sso-saml).
access/security.rsAdmin security tools: configurable security policy (password rules, session lifetimes, login rate limit), IP ban list + admin IP allow-list (middleware), "Login as client / user" impersonation with audit and a return path, and client close / merge.
access/security_headers.rsBaseline security headers on every response. Handlers that already set a stricter value (e.g. file downloads with sandbox CSP) keep theirs.
access/trusted_proxy.rsTrusted proxies (WHMCS System > Trusted Proxies, plus PELTY_TRUSTED_PROXIES): when the TCP peer is a trusted proxy, the client address is taken from X-Forwarded-For (read right to left, skipping other trusted hops), else from X-Real-IP / CF-Connecting-IP. Every later layer (IP rules, rate limits, sessions, audit) then sees the real client in ConnectInfo, and X-Forwarded-Proto becomes a ClientScheme extension. From any other peer these headers are removed, so nothing downstream can be fooled.
addon_host/addon_events.rsAddon event lifecycle (ABI 0.3) and mixins.
addon_host/addon_ext.rsWASM addon extension points beyond the per-kind contracts: synchronous pre-action hooks, addon HTTP routes under /api/v1/addons/<id>/… and declarative admin/client UI. All of them go through the addon's call export (ABI 0.2): op hook, op http. See make docs.
addon_host/addon_filters.rsValue filters (capabilities.filters): the host passes a value through every subscribed addon in priority order (op filter, input {filter, value, context}, reply {value}). A reply is kept only when it has the same JSON schema as the value; money keys follow the money.modify rules. Filters always fail open.
addon_host/addon_functions.rsCore-function mixins: addons wrap a named host function (pelty_extensions::functions::FUNCTIONS) with a mixin whose target is fn <name>. Op mixin, input {function, at, phase: "before", context, input} → reply {input?, state?}, then {function, at, phase: "after", context, input, output, state} → reply {output?}. Replies keep the JSON schema and never change money values; mixins fail open and honour the per-addon kill switch.
addon_host/addon_health.rsAddon call isolation: every hook, mixin and filter call goes through guarded_call, which - gives the wasm store the listener's own deadline and fuel (the store is interrupted, not merely abandoned, when the budget runs out), - records per-addon statistics (calls, failures, timeouts, latency), and - trips a per-addon circuit breaker after PELTY_ADDON_BREAKER_FAILS (default 5) consecutive failures: the addon is skipped for PELTY_ADDON_BREAKER_COOLDOWN_SECS (default 60) and then retried.
addon_host/addon_power.rsHost side of the addon power interface: core reads with fixed column lists, typed audited writes, SQL limited to the addon's own tables, and background jobs. The runtime has already checked the capability; every function here re-checks its own scope (defence in depth).
addon_host/addon_secrets.rsAddon settings of kind secret (API secrets, OAuth client secrets) are sealed at rest with the app encryption key and never returned to the browser: reads get MASK, and saving MASK back keeps the old value.
addon_host/addon_trust.rsOperator trust is separate from package hashes and publisher-supplied metadata.
addon_host/addon_upgrade.rsPre-extraction replacement policy. In-place upgrades never change schema or approvals.
addon_host/addons.rsAddon system: catalog (built-in + on-disk manifests), install / enable / disable / uninstall, schema-validated settings and the runtime gate that keeps uninstalled provisioning modules and gateways from running. See make docs.
addon_host/mod.rsAddon runtime host: loading WASM addons, their events, filters, functions, secrets, trust, health, upgrades and plugin hooks.
addon_host/plugin_hooks.rsDurable webhook delivery of domain events to external plugins.
addon_host/wasm_addons.rsRuntime side of WASM addons: compile the component of an installed and enabled addon and register it with the matching host registry (gateways, provisioning, registrars) or keep it for captcha / SSO lookups. Disabling or uninstalling unregisters it again. See make docs.
comms/chat_bus.rsPresence and durable-message invalidation hints. Transcript content stays in Postgres.
comms/event_mail.rsCustomer emails driven by domain events (WHMCS "email on event"): welcome, order confirmation, payment receipt, refund, service lifecycle and ticket opened. Each send respects the operator's disabled list.
comms/mail.rsRendering + queueing of transactional email. Requests only enqueue an email.send job; the worker renders and delivers it, so SMTP latency or outages never block a request and failures retry with backoff.
comms/mailbox.rsMailbox fetchers for ticket piping: minimal POP3 (RFC 1939) and IMAP4rev1 (RFC 3501) clients plus a small RFC 5322/MIME reader. The clients run over any async stream, so tests drive them with an in-memory scripted server; production connects with TcpConnector (TLS via rustls + webpki roots).
comms/mod.rsCommunication: outgoing mail, inbound mailbox/pipe, notifications, event mail, outgoing webhooks, the chat bus and support automation.
comms/notify.rsStaff notifications (Slack / Discord / Teams / Telegram / SMS gateway / email-to-SMS / HMAC-signed generic webhook) fanned out from domain events through the job queue, with a per-delivery log. The HTTP transport is a trait so tests can record deliveries instead of reaching the network.
comms/pipe.rsShared inbound email intake for department mailboxes, signed webhooks and local MTA pipes. All transports use the same sender and upload policy.
comms/support_ext.rsSupport automation: department auto-replies and mailbox piping runs.
comms/webhooks_out.rsOutgoing webhooks: every domain event is POSTed to each enabled webhook_endpoints row whose event filter matches (*, invoice.* or an exact name).
error.rsError → HTTP. Handlers return ApiError; the localize_errors middleware turns the stable code into a translated JSON body with the request id.
hosting/cluster.rsSeveral instances behind one Redis (PELTY_REDIS_URL): change notifications and the live instance list. Without Redis every function here is a no-op and an instance only sees its own changes (as before).
hosting/domains.rsDomain lifecycle driven by the job queue: register/transfer paid orders, apply paid renewals, raise auto-renew invoices, expire lapsed domains and send expiry reminders. Registrar calls go through RegistrarModule.
hosting/mod.rsHosting backends: provisioning, domains and registrant verification, SSL probing and the cluster coordinator.
hosting/provision.rsProvisioning job runner: loads the service + server, decrypts the panel key, runs the module action and records the outcome. Transient errors are retried by the queue; config errors and 4xx rejections go dead at once.
hosting/registrant_verify.rsICANN WHOIS accuracy: after a registration, transfer or registrant e-mail change the registrant must confirm their address within 15 days. An address confirmed once is trusted for later domains. Reminders go out 10 and 5 days before the deadline; past it the request is overdue (the registrar suspends such names on its side; staff see it here).
hosting/ssl_probe.rsTLS certificate probe for client domains (WHMCS "SSL Sync"). Connects to port 443, verifies the chain against webpki roots and reads the leaf certificate's validity window and issuer. Private and loopback addresses are refused so a client domain cannot point the probe inward.
lib.rsHTTP layer: routing, extractors, error mapping. Business logic lives in service crates; SQL lives in pelty-database.
money/autopay.rsDaily billing.autopay job: charge due invoices off-session with the customer's default saved method, retry on the configured schedule and mail a dunning notice after each failed attempt.
money/einvoice.rsE-invoicing: UBL 2.1 export following Peppol BIS Billing 3.0 (EN 16931), optionally with the Romanian CIUS-RO customization used by RO e-Factura.
money/exchange_rates.rsDaily exchange-rate update: fetch the configured feed through the swappable HTTP transport, apply rates (+ markup) and optionally re-sync prices. Admin routes live in routes::currencies.
money/mod.rsBilling engine pieces outside HTTP: overdue processing, autopay, exchange rates, e-invoices, PDF rendering and VIES VAT checks.
money/overdue.rsGlue for the daily billing.overdue job: runs the DB automation, then mails reminders / suspension notices, queues panel actions and emits events.
money/pdf.rsServer-side invoice PDF (A4, embedded subset DejaVu Sans, full Unicode text incl. Romanian diacritics). Pure layout from an already-localized InvoiceDoc; no I/O.
money/vies.rsEU VAT number verification against VIES (REST API) through the swappable HTTP transport. The result is stored on the customer and, when the tax setting require_vies is on, gates the reverse charge.
platform/approval.rsOrder auto-approval (rules evaluated on order.created / invoice.paid), shared order activation, and the daily pre-due invoice reminders.
platform/backup.rsDatabase backups via pg_dump -Fc, run as a background job. Files live in PELTY_BACKUP_DIR (default ./backups); names are generated server-side.
platform/changes.rsGlue between payments / the scheduler and plan changes & cancellations.
platform/cron.rsCron expressions for scheduled tasks (built-in and <addon>:<op>).
platform/csv.rsMinimal RFC 4180 CSV writer with spreadsheet formula-injection guard.
platform/dev_docs.rsServes the generated docs (make docs → target/docs) at /docs: the user docs at /docs/, the developer docs at /docs/developers/. Staff only unless PELTY_DOCS_PUBLIC is true.
platform/extract.rsSession authentication, CSRF and permission extractors.
platform/mod.rsPlatform services: the background worker and cron, backups, updates, change log, approvals, CSV/extract helpers, developer docs, the WHMCS-compatible API, translations and static files.
platform/static_files.rsStatic file serving for frontend bundles and theme assets.
platform/translations.rsTranslation editor (WHMCS "Translation editor"): staff override any shipped string per language; overrides win over locales/*.json in the UI (/i18n/{lang}) and in server-rendered text (mails, errors).
platform/updates.rsUpdate checker ("automatic updates", WHMCS system/updates).
platform/whmcs_api.rsWHMCS-compatible API (POST /includes/api.php, also /api/v1/compat/whmcs), see make docs.
platform/worker.rsBackground job worker over the Postgres queue. Multiple server instances can run it concurrently (SKIP LOCKED); the daily recurring-billing job is deduplicated by its idempotency key.
routes/clients/account.rsPassword reset and email verification. Every public endpoint here is rate-limited (AuthGuard) and never reveals whether an account exists.
routes/clients/affiliate_extras.rsAffiliate extras: per-product rates, tiers, promotional materials.
routes/clients/affiliates.rsAffiliate program: public referral link (sets a tracking cookie), client dashboard + payout requests, admin management (affiliates.manage).
routes/clients/avatars.rsProfile pictures. Uploads are sniffed by magic bytes (PNG/JPEG/WebP only, must match the declared Content-Type), decoded with dimension and memory limits, centre-cropped and re-encoded to PNG, so no original bytes (metadata, appended payloads, polyglots) are ever stored or served.
routes/clients/banned_emails.rsAdmin CRUD for banned e-mail domains (checked at registration).
routes/clients/client_extras.rsClient notes and custom client field values (customers.manage), field definitions (customers.manage), the client's own editable fields, and the admin to-do list (todos.manage).
routes/clients/client_group_rules.rsAutomatic client-group assignment rules (client_groups.manage).
routes/clients/client_groups.rsAdmin client groups (client_groups.manage).
routes/clients/customer_workspace.rsUnified customer workspace reads; section permissions use scoped CurrentUser permissions.
routes/clients/customers.rsCustomer profiles. /client/* is always scoped to the caller's own customer record; /admin/customers* requires customers.manage.
routes/clients/fraud.rsAdmin fraud screening: settings, results and the manual review queue.
routes/clients/gdpr.rsGDPR: self-service data export and erasure requests; admin review. /account/* is session-only (API keys are refused by the extractor).
routes/clients/kyc.rsKYC endpoints: customers upload identity documents (images/PDF, sniffed by magic bytes), staff review them; ordering can be gated on the result.
routes/clients/mod.rsClient HTTP routes: customers, workspaces, groups, client extras, KYC, GDPR, fraud, banned emails, affiliates, avatars and the client account.
routes/commerce/bundles.rsBundles and upsell / cross-sell: public catalog reads, admin management (catalog.manage), and the order-time bundle check.
routes/commerce/catalog.rsCatalog: public listing and admin management (catalog.manage).
routes/commerce/data_feeds.rsData feeds (WHMCS feeds/productsinfo.php, domainprice.php): tiny public JavaScript snippets that print a product's name, description or price on an external site via <script src=...>.
routes/commerce/mod.rsCommerce HTTP routes: catalog, bundles, order forms, the public store and data feeds.
routes/commerce/order_forms.rsOrder form templates (admin CRUD, per-group template) and product free-domain rules; the public form lookup for the client order page.
routes/commerce/store.rsPublic storefront (no login): carts with a live price/tax preview, storefront checkout for a signed-in customer, the client order status page, the client home widgets, contacts / sub-accounts, email preferences, login history and the cart-recovery email job.
routes/config/automation.rsAutomation (cron) dashboard: the built-in scheduled tasks, their last outcome and next run, plus a manual "run now" (jobs.manage).
routes/config/general_settings.rsGeneral Settings (WHMCS Setup → General Settings) and admin-only custom code.
routes/config/i18n.rsTranslation HTTP routes: serve UI string bundles per locale to the frontend.
routes/config/mod.rsSettings HTTP routes: settings, general settings, automation, site/branding, i18n and the setup wizard.
routes/config/settings.rsAdmin-editable system settings (non-secret).
routes/config/setup_wizard.rsFirst-run setup wizard / getting-started checklist.
routes/config/site.rsPublic website configuration (brand, menus, footer, homepage) and CMS pages made of declarative blocks rendered natively by the frontend.
routes/extensibility/extensions.rsThemes, plugins and gateway administration.
routes/extensibility/headless.rsHeadless commerce API: the whole purchase flow (catalog → cart → checkout → payment → status) for custom storefronts, mobile apps and integrations. Authenticated with a customer API key (Authorization: Bearer pelty_…); interactive sessions are refused so the flow is never CSRF-exposed and the checkout captcha (meant for browsers) does not apply. Prices are always computed server-side.
routes/extensibility/mod.rsExtension HTTP routes: plugins, extensions, vendor data and the headless API.
routes/extensibility/plugins.rsPlugin extension points over HTTP: - GET /plugins/ui UI slot entries for the current user - GET /admin/plugins/{id}/settings schema + current values - PUT /admin/plugins/{id}/settings validated against the schema - /plugins/{id}/... routes of built-in modules, only reachable while the plugin is enabled (see modules).
routes/extensibility/vendor.rsVendor marketplace (MarketConnect-style resale): sync a vendor server's catalog through its provisioning module, import items as products with a markup, and re-price them. Ordering/provisioning use the normal flow.
routes/finance/billable.rsBillable items, one-off draft invoice lines, and the per-product "generate invoice N days before due" setting.
routes/finance/billing.rsOrders, invoices, services and jobs. Every /client/* handler resolves the caller's customer from the session and filters by it in SQL, so a customer can never read another customer's records (404, not 403, to avoid leaking existence).
routes/finance/coupons.rsCoupons: admin management (coupons.manage) and a client-side code check.
routes/finance/credit.rsCustomer credit balance: client view + apply to invoice, staff adjust.
routes/finance/currencies.rsCurrency administration (settings.manage): exchange rates, default currency, and price recalculation from the default currency.
routes/finance/einvoice.rsE-invoicing: UBL 2.1 / Peppol BIS 3.0 (optionally CIUS-RO for RO e-Factura) download for issued invoices, credit notes and debit notes, plus the legal-field settings. See crate::einvoice for the builder.
routes/finance/gateways.rsPayment gateway administration (enable/disable, settings rendered from each gateway's schema, custom HTTP gateways, test-webhook tool, delivery log) and the client-side gateway picker / return-time completion.
routes/finance/invoice_ops.rsInvoice merge / split / duplicate / notes (staff), client mass-pay merge, and gift cards (staff issue, client redeem).
routes/finance/invoice_pdf.rsBranded invoice PDF download (client: own invoices; staff: any) and the branding settings.
routes/finance/legal_entities.rsLegal entities (selling companies): numbering, terms, bank details.
routes/finance/mod.rsBilling HTTP routes: invoices (admin, client, PDF, e-invoice), quotes, payments, gateways, credit, coupons, tax, currencies, refunds, renewals, overdue rules and usage billing.
routes/finance/overdue.rsAdmin overdue automation settings and a manual run.
routes/finance/payment_methods.rsSaved payment methods, the auto-pay toggle and auto-pay settings.
routes/finance/quote_pdf.rsQuote PDF download (client: own sent/decided quotes; staff: any), rendered with the same branded layout as invoices.
routes/finance/quotes.rsQuotes / proposals: admin drafts and sends (quotes.manage); the customer views, accepts (→ finalized invoice) or declines.
routes/finance/refunds.rsRefunds against payments (partial or full) through the payment gateway, and the credit notes they produce.
routes/finance/renewals.rsOn-demand renewals (WHMCS Billing > On-Demand Renewals): a client or staff member invoices the next period of a service ahead of the cron.
routes/finance/tax.rsAdmin tax configuration: global settings and rules per country/region.
routes/finance/usage.rsMetered billing: metric catalog (catalog.manage), usage ingestion and period history (services.manage, API keys work too), client view.
routes/health.rsHealth check routes (/health, readiness) mounted at the root without the API prefix.
routes/helpdesk/content.rsKnowledge base and announcements: public reads (published only) and the staff editor (content.manage). Bodies are plain text / Markdown; the frontend renders them as text, never as raw HTML.
routes/helpdesk/downloads.rsDownloads area: staff upload (downloads.manage, raw body, ≤ 25 MiB, allow-listed types by magic bytes); customers download what their active services entitle them to. Files are always served as attachments.
routes/helpdesk/live_chat.rsLive chat: public widget endpoints (bearer token per session) and the staff inbox. Both sides poll ?after=<last id>.
routes/helpdesk/live_chat_admin.rsStaff side of live chat: list, claim and answer chats. Child of live_chat.
routes/helpdesk/live_chat_ws.rsWebSockets accept secrets only in the first frame, never a URL or protocol. Durable writes remain REST operations with the existing CSRF protections.
routes/helpdesk/mod.rsSupport HTTP routes: tickets (statuses, SLA, macros, attachments, AI), live chat, network status, downloads, knowledge content and projects.
routes/helpdesk/network_status.rsNetwork / service status issues: public status page and staff editor (status.manage). Every status change is recorded as a timeline update.
routes/helpdesk/projects.rsProject management (WHMCS Project Management addon).
routes/helpdesk/support.rsSupport desk extras (tickets.manage): canned replies, tags, merge/split, escalation rules, SLA metrics, staff list; client ratings.
routes/helpdesk/support_ext.rsSupport extensions: department piping/auto-reply settings, spam rules, ticket time tracking, KB tags, announcements RSS and the contact form.
routes/helpdesk/ticket_ai.rsOpt-in, staff-requested drafts. No message, mail or job is ever created here.
routes/helpdesk/ticket_attachments.rsTicket attachment upload/download. Type is decided by magic bytes (never by the client's name or Content-Type alone); the declared type must agree with the sniffed one. Files are only ever served as downloads with nosniff and a sandbox CSP.
routes/helpdesk/ticket_macros.rsTicket macros: manage them, list the ones offered on a ticket (with the reply already filled in), and apply one.
routes/helpdesk/ticket_sla.rsTicket SLA policies (department × priority), live SLA state, the at-risk list and the warning / breach scan.
routes/helpdesk/ticket_statuses.rsCustom ticket statuses: everyone signed in can read them (for labels), staff with tickets.manage manage them.
routes/helpdesk/tickets.rsSupport tickets: client area (own tickets only, no internal notes) and staff desk (tickets.manage).
routes/hosting/mod.rsHosting HTTP routes: provisioning, server modules, service upgrades/downgrades and SSL.
routes/hosting/modules_ext.rsModule extras: server health, admin custom module buttons, the client module panel, the module log, the failed-provisioning queue and the public licensing verify endpoint.
routes/hosting/provisioning.rsProvisioning admin API: modules, panel servers (keys write-only), product module binding and per-service module actions / status.
routes/hosting/service_changes.rsUpgrade/downgrade and cancellation requests (client + admin).
routes/hosting/ssl.rsSSL certificate status for client domains: admin overview, on-demand recheck, and the client's own view.
routes/identity/admin_security.rsAdmin account security: step-up re-authentication ("sudo mode") and an authenticated password change (needed once a password has expired). The enforcement itself lives in RequirePermission (see extract.rs).
routes/identity/api_keys.rsPersonal API keys. Creating, listing and revoking keys requires an interactive session (keys cannot manage keys). The secret is shown once.
routes/identity/auth.rsLogin, logout, register and session routes for clients and staff.
routes/identity/mfa.rsTOTP two-factor authentication: enrolment, login challenge, backup codes.
routes/identity/mod.rsIdentity HTTP routes: login/register, MFA, passkeys, SSO, API keys, staff and security questions.
routes/identity/passkeys.rsPasskeys (WebAuthn, webauthn-rs): enrolment, passwordless login and step-up re-auth. Recovery when a passkey is lost is the normal password (+ TOTP / backup code) login; staff can revoke a user's passkeys.
routes/identity/security_questions.rsSecurity questions: staff manage the list, users pick one and store a hashed answer, staff verify a caller against it (answer never shown).
routes/identity/sso.rsClient SSO into the provisioned panel via a signed one-time token. POST /client/services/{id}/sso returns the redirect URL; POST /sso/consume lets the panel burn the token exactly once.
routes/identity/staff.rsStaff roles/permissions editor, staff list with role assignment and the staff 2FA policy (users.manage).
routes/messaging/campaigns.rsMass mail campaigns (campaigns.manage), the client's marketing opt-out and the public, scanner-safe unsubscribe endpoint.
routes/messaging/email_log.rsWHMCS "Email Message Log" (staff, all mail with body and resend) and the client area "Email History" (own mail only).
routes/messaging/email_templates.rsAdmin editing of email templates. Overrides are stored per (template, locale); deleting one reverts to the locale-file default.
routes/messaging/link_tracking.rsLink tracking (WHMCS Products > Link Tracking): admin CRUD + public redirect.
routes/messaging/mod.rsMessaging HTTP routes: email templates and log, SMTP, campaigns, link tracking, webhook endpoints and inbound webhooks.
routes/messaging/smtp.rsAdmin SMTP settings. Stored in system_settings under mail.smtp; the password is sealed with the app SecretBox (AES-256-GCM) and never returned. When enabled these settings replace PELTY_SMTP_URL.
routes/messaging/webhook_endpoints.rsAdmin: outgoing webhook endpoints, their delivery log and redelivery (webhooks.manage). See crate::webhooks_out for the delivery rules.
routes/messaging/webhooks.rsInbound payment webhooks: verify → dedupe → apply → mark processed.
routes/mod.rsHTTP API router. Route modules are grouped by domain (finance, commerce, hosting, registry, clients, identity, helpdesk, messaging, extensibility, config, ops) and re-exported here so crate::routes::<module> keeps working. api_router merges every domain router. Covered by the integration suite and the pelty-docs route inventory tests.
routes/openapi.rsOpenAPI document (utoipa) listing documented handlers; served at /api/openapi.json.
routes/ops/admin.rsAdmin dashboard routes: overview stats and admin landing data.
routes/ops/admin_ops.rsAdmin ops: dashboard prefs + quick links, login history, staff online, calendar, per-client email log with resend, DB backups.
routes/ops/admin_tools.rsAdmin productivity: global search (command palette), System Health, personal "My Notes" and staff notification rules (Slack/Discord/Teams/webhook).
routes/ops/bulk.rsBulk invoice actions, client mass-pay (from account credit), pre-due reminder settings, and the order approval queue + rules.
routes/ops/csv_import.rsCSV import (clients, services, invoices): analyze → map columns → dry-run → import.
routes/ops/exports.rsTax report (JSON or CSV) and invoice / payment CSV exports.
routes/ops/mass.rsAdmin list mass actions (WHMCS "With selected…") for clients, services, tickets and domains. Invoices and orders use bulk.rs. Each id is handled on its own and reported back, so one bad row never blocks the rest.
routes/ops/mod.rsAdmin operations HTTP routes: dashboard/admin, admin tools, reports, exports, CSV import, bulk and mass actions.
routes/ops/reports.rsAdmin financial reports: revenue, refunds, MRR and churn by month.
routes/registry/domain_pricing.rsRegistrar TLD price sync + margin rules, premium-name settings and per-TLD additional order fields.
routes/registry/domains.rsDomains: client search/order/manage, admin TLD pricing and domain admin (services.manage). Registrar work runs in the domains.* jobs.
routes/registry/mod.rsDomain HTTP routes: domain registration/management and TLD pricing.
shared.rsOptional Redis (PELTY_REDIS_URL) shared by every instance: rate-limit counters and the scheduler leader lock. Without it (or while it is unreachable) everything stays local: in-memory limits, and every instance with PELTY_SCHEDULER=true schedules (jobs are deduplicated in Postgres). Cross-instance invalidation and the instance list live in crate::cluster.

#pelty-auth

Authentication: Argon2id passwords, session tokens, rate limiting.

ModuleWhat it does
crypto.rsApplication-level encryption for small secrets at rest (TOTP seeds). AES-256-GCM with a random 96-bit nonce; key from PELTY_ENCRYPTION_KEY (base64, 32 bytes). There is no built-in default key.
lib.rsAuthentication: Argon2id passwords, session tokens, rate limiting.
password.rsArgon2 password hashing and verification.
policy.rsAdmin-configurable security policy (security.policy system setting): password rules, session lifetimes and the login rate limit. Read through a short in-process cache; invalidate after an update.
rate_limit.rsIn-memory fixed-window rate limiter (per key, e.g. IP). Single-node only.
service.rsBusiness logic for register/login/logout. HTTP-agnostic.
token.rsRandom tokens and their SHA-256 hashes for sessions and links.
totp.rsRFC 6238 TOTP (SHA-1, 6 digits, 30 s) plus backup codes.

#pelty-billing

Pure billing domain logic: billing cycles, invoice math, service lifecycle. No I/O here so every rule is unit-testable; SQL lives in pelty-database.

ModuleWhat it does
cycle.rsBilling cycles (monthly, annually, ...) and date arithmetic for renewals.
lib.rsPure billing domain logic: billing cycles, invoice math, service lifecycle. No I/O here so every rule is unit-testable; SQL lives in pelty-database.
lifecycle.rsService lifecycle state machine. Mirrored by the services_lifecycle_guard trigger so illegal transitions fail even if app code is bypassed.
math.rsExact invoice math with rust_decimal. Rounding: half away from zero (commercial rounding) to the currency exponent, per line, then summed, //! so the printed lines always add up to the printed total.
proration.rsDay-based proration for mid-period plan changes. Exact decimals only.
tax.rsTax engine: rules per country/region, inclusive or exclusive prices, compound taxes and EU VAT reverse charge. Exact decimal; every tax amount is rounded to the currency exponent per line, so lines always add up.
vat_format.rsOffline VAT number format validation (EU + UK), run before VIES. Checksums are verified for RO, DE, GB, IT, BE, NL, PL, ES-free formats get only a length/charset check. This does NOT prove the number is registered; VIES remains the authority.

#pelty-cli

pelty-cli, developer tooling for PeltyCMS addons.

ModuleWhat it does
main.rspelty-cli, developer tooling for PeltyCMS addons.
wasm_scaffold.rspelty-cli addon wasm-scaffold: a WASM addon skeleton (Rust tested; C and TinyGo are templates only, not built in CI).

#pelty-core

Core domain primitives shared by all PeltyCMS crates.

ModuleWhat it does
config.rsEnvironment-based configuration, validated at startup.
error.rsTyped application errors. Every error has a stable message code that is translated via locale files; internal details are logged, never returned.
i18n.rsTranslations from flat JSON locale files (locales/<lang>.json). en is the base; missing keys fall back to en, then to the key itself.
lib.rsCore domain primitives shared by all PeltyCMS crates.
money.rsExact money type. Never use floats for money.

#pelty-database

PostgreSQL pool, migrations and repositories. All SQL lives here.

ModuleWhat it does
auth/api_keys.rsPersonal API keys (hashed, scoped, revocable, optional expiry).
auth/mfa.rsStorage for TOTP 2FA: sealed secrets, hashed backup codes, login challenges.
auth/mod.rsIdentity repositories: users, staff, sessions, MFA, passkeys, API keys, OAuth, SAML/SSO and security settings.
auth/oauth_provider.rsStorage for PeltyCMS acting as an OAuth 2.0 / OIDC provider. Codes and tokens are passed in already hashed; nothing here sees a raw secret.
auth/passkeys.rsPasskey storage and single-use WebAuthn ceremony state. The crypto lives in pelty-api (webauthn-rs); this module stores opaque JSON.
auth/saml.rsSAML 2.0 SP storage (migration 0289): IdPs, pending requests, replay cache and IdP-session links for single logout.
auth/security.rsSecurity / accounts suite storage (migration 0240): captcha nonces, OAuth states + linked identities, IP rules, impersonation sessions, WHMCS-compatible API credentials, versioned ToS, client close/merge.
auth/security_questions.rsSecurity questions (WHMCS Clients > Security Questions). Answers are stored as password hashes of a normalised form and never returned.
auth/sessions.rsSession repository: create, look up and revoke login sessions.
auth/sso.rsOne-time SSO nonces (see pelty_provisioning::sso).
auth/staff.rsStaff roles/permissions editor, role assignment and the staff 2FA policy.
auth/user_tokens.rsOne-time user tokens (password reset, email verification). Only the SHA-256 of the token is stored; consuming is atomic and single-use.
auth/users.rsUser repository: accounts for clients and staff.
billing/add_funds.rsAdd funds: a client-requested invoice whose payment is credited to the account balance (see migration 0272 trigger). Limits live in settings.
billing/autopay.rsSaved payment methods and auto-charge of due invoices. Attempts are unique per (invoice, attempt index), so a run is idempotent and two workers can never charge the same attempt twice.
billing/billable.rsBillable items: one-off charges (held, added to the client's next renewal invoice, or invoiced immediately) and recurring charges invoiced by the daily billing job.
billing/card_expiry.rsSaved-card expiry notices (WHMCS --CreditCardExpiryNotices).
billing/coupons.rsCoupons / promo codes: percent or fixed (per eligible item), first invoice only or recurring, global and per-customer limits, validity window and product restrictions. Redemption happens inside the order transaction with the coupon row locked, so limits hold under concurrency.
billing/credit.rsCustomer credit: an append-only ledger per currency. Staff adjust it; credit is spent by applying it to an unpaid invoice, which records a credit gateway payment through the normal (exactly-once) payment path.
billing/currencies.rsCurrency administration: exchange rates against the default currency, and converting base-currency product prices into other currencies.
billing/exchange_rates.rsAutomatic exchange-rate updates: feed parsing (ECB XML or a JSON {"base": "EUR", "rates": {...}} document) and applying the rates, with an optional markup, relative to the default currency. Fetching lives in the API crate behind the mockable HTTP transport.
billing/gateway_admin.rsGateway administration: config upserts, custom gateways, webhook delivery log, payment lookups for return-time completion.
billing/gateway_rules.rsPer-gateway fees, min/max amounts and currency restrictions. All money is Decimal; limits and fixed fees are per currency (no FX conversion).
billing/gift_cards.rsGift cards / prepaid credit codes. Codes are shown once at creation and stored as SHA-256; redeeming moves the full balance to account credit.
billing/invoice_ops.rsInvoice operations that WHMCS staff use daily: merge, split, duplicate, notes. Finalized invoices are immutable, so merge/split create new finalized invoices holding *copies* of the lines (copied_from) and void the sources (merged_into / split_from keep the trail). Copies do not re-claim a service period, so renewals stay idempotent.
billing/invoice_tools.rsBulk invoice helpers and pre-due invoice reminder settings/run.
billing/invoices.rsInvoices, items, finalization (gapless numbering), credit notes and idempotent payment application. Finalized invoices are immutable; the DB triggers in 0004 enforce it even if this code had a bug.
billing/invoices_service.rsInvoiceService: Invoice reads and pool-level invoice actions (void, credit/debit notes, payments).
billing/legal_entities.rsLegal entities (selling companies) with per-year invoice numbering.
billing/mod.rsBilling repositories: invoices, payments, credit, coupons, tax, currencies and gateways.
billing/overdue.rsOverdue invoice automation: reminders, a late fee as a separate invoice, suspension of linked services, and unsuspension once paid. Every action is recorded in overdue_actions (unique per invoice + action), so a run is idempotent and safe to repeat.
billing/quotes.rsQuotes / proposals: draft → sent → accepted (becomes a finalized invoice) or declined. Line math reuses the invoice rounding rules.
billing/refunds.rsRefunds against payments. Two phases so a gateway call never runs inside a DB transaction: reserve locks the payment and records a pending refund (capacity is checked under the lock, so concurrent refunds can never exceed the payment), then complete / fail settle it.
billing/tax.rsTax rules, tax settings and applying the tax engine to a draft invoice right before it is finalized. The result (rates, breakdown, customer snapshot) is frozen with the invoice by the triggers in 0096.
billing/transactions.rsAdmin transactions list (all gateway payments) with filters and totals.
billing/usage.rsMetered (usage) billing. Records land in the service's open period; when the period ends (the service's billing cycle) it is invoiced once: per metric max(0, sum - included) × unit_price, rounded to the currency.
clients/affiliate_extras.rsAffiliate extras: per-product commission rates, referral-count tiers and promotional materials. Accrual itself stays in the affiliate_accrue trigger.
clients/affiliates.rsAffiliates: referral codes, signup attribution, commissions (accrued by a DB trigger when a referred customer's invoice is paid) and payouts.
clients/avatars.rsProfile pictures (already validated and re-encoded by the API layer).
clients/banned_emails.rsBanned e-mail domains (WHMCS "Banned Emails").
clients/client_extras.rsStaff notes on clients, custom client fields (with per-client values) and the admin to-do list.
clients/client_group_rules.rsAutomatic client-group assignment rules (first match by priority wins).
clients/client_groups.rsClient groups. A customer belongs to at most one group.
clients/customer_workspace.rsPermission-filtered, allowlisted customer workspace projections.
clients/customers.rsCustomers: billing profile owned by a user account (or staff-created).
clients/customers_service.rsClientService: Client (customer) records and their contacts.
clients/fraud.rsFraud screening settings, rule inputs and stored results.
clients/gdpr.rsGDPR data export and erasure.
clients/inactive_clients.rsRetention-based erasure of inactive clients (WHMCS Data Retention "delete inactive clients"). Clients are anonymised with the same routine as a GDPR erasure (invoices stay for tax law), never hard deleted. Safeguards: off by default, at least 365 days, never staff, never anyone with live services/domains, unpaid invoices, open tickets or a pending GDPR request, at most max_per_run per run, and every erasure leaves a completed GDPR request as the audit trail.
clients/kyc.rsKnow-your-customer: document uploads, staff review and the derived customer status used to gate ordering (configurable, off by default).
clients/mod.rsClient repositories: customers, workspaces, groups, KYC, GDPR, fraud checks and affiliates.
commerce/bundles.rsBundles and upsell / cross-sell recommendations.
commerce/catalog.rsProduct catalog: groups, products, per-cycle/currency prices, config options. Listing is done in a fixed number of queries (no N+1).
commerce/mod.rsCommerce repositories: products, bundles, order forms, the public storefront and orders (placing and approval).
commerce/order_approval.rsOrder approval rules and the pending-order approval queue.
commerce/order_forms.rsOrder form templates (WHMCS "order form templates"): which products or groups a form sells, its step layout, extra fields and the post-order redirect. Groups can pin a form; the client order page renders it.
commerce/orders.rsOrders: cart validation → order + finalized invoice in one transaction; approval creates services in one transaction.
commerce/orders_service.rsOrderService: Orders: placing, listing and changing order state.
commerce/storefront.rsPublic storefront: persisted carts with a read-only price/tax preview, abandoned-cart lookup, customer contacts (sub-accounts), email preferences, login history and admin order notes.
config/automation_settings.rsPer-task automation settings: enable flag, legacy start hour and an optional cron expression (company time zone).
config/branding.rsInvoice branding shown on PDF invoices.
config/content.rsKnowledge base and announcements. Public reads only ever return published rows; locale NULL matches every language.
config/general_settings.rsWHMCS "General Settings": general, system URLs, maintenance mode, localisation, ordering (security lives with auth settings, social links with the website footer), plus admin-only custom CSS/JS. Stored as JSON in settings; every section has serde defaults so older rows keep loading after new fields are added.
config/mod.rsSettings repositories: key/value settings, typed general settings, automation, branding, site content, translations and the setup wizard.
config/settings.rsKey/value settings repository used by every typed settings module.
config/setup_wizard.rsFirst-run setup wizard (WHMCS Setup Wizard / "Getting started"): a checklist computed from live configuration, plus a dismiss flag.
config/site.rsPublic website CMS pages.
config/translations.rsTranslation editor overrides.
extensibility/addon_kv.rsStorage for the WASM addon key/value host functions.
extensibility/addon_registry.rsInstalled-addon registry (see make docs).
extensibility/addons.rsProduct addons and custom order fields.
extensibility/extensions.rsPlugin state, gateway config, webhook dedup, payments.
extensibility/mod.rsExtension repositories: installed addons, addon key/value storage, the addon registry and vendor data.
extensibility/vendor.rsMarketConnect-style vendor resale: a vendor server's catalog is synced here, staff import items as normal products (module + server + price = cost + markup), and orders run through the normal provisioning lifecycle.
helpdesk/downloads.rsDownloads area. Bytes live in the database; access requires an active service of a linked product (or no linked products at all).
helpdesk/live_chat.rsLive chat sessions and messages (polling; ids are monotonic so clients fetch after the last id they have).
helpdesk/live_chat_ext.rsSettings, assignment and atomic transcript conversion for live chat.
helpdesk/mod.rsSupport repositories: tickets and their statuses/SLA/macros/attachments, live chat, satisfaction, network status, announcements, downloads and projects.
helpdesk/network_status.rsNetwork / service status issues and their timeline updates.
helpdesk/noticeboard.rsStaff noticeboard: notices visible to every staff member.
helpdesk/projects.rsProject management: projects for a client with tasks, time tracking and links to tickets and invoices; billable time is invoiced as lines.
helpdesk/satisfaction.rsSupport satisfaction (CSAT) report from ticket ratings.
helpdesk/support.rsSupport desk extras on top of tickets: canned replies, tags, client ratings, merge/split, escalation rules, SLA metrics and e-mail piping log.
helpdesk/support_ext.rsSupport extensions: department auto-reply + mailbox settings, spam rules, time tracking, KB tags and contact form messages.
helpdesk/ticket_attachments.rsTicket attachments: pending uploads are owned by the uploader and linked to a message atomically inside the reply transaction.
helpdesk/ticket_macros.rsTicket macros: a canned reply or note plus status, priority, assignee, department and tag changes applied in one step.
helpdesk/ticket_sla.rsTicket SLA policies (per department and priority), live SLA state, and the warning / breach scan with escalation.
helpdesk/ticket_statuses.rsAdmin-defined ticket statuses (built-ins are seeded by the migration).
helpdesk/tickets.rsSupport tickets. Client queries always filter by customer_id in SQL; internal notes are only returned when include_internal is set (staff).
helpdesk/tickets_service.rsTicketService: Tickets, their messages and support departments.
hosting/mod.rsService repositories: active services, upgrades/downgrades, provisioning state, server import and SSL checks.
hosting/module_ext.rsServer health, module log, the failed-provisioning queue and licensing lookups.
hosting/provisioning.rsProvisioning persistence: panel servers (API keys sealed at rest), the per-service remote state, and enqueueing module actions as durable jobs.
hosting/server_import.rsImport accounts that already exist on a provisioning server as services (WHMCS "Server Sync / Import"). Accounts come from the module's list_accounts; this module only validates and writes.
hosting/service_changes.rsPlan changes (upgrade / downgrade) and cancellation requests.
hosting/services.rsServices (provisioned products) with lifecycle and renewal billing.
hosting/services_service.rsHostingService: Active customer services (hosting accounts, products) and their state.
hosting/ssl_checks.rsStored results of TLS certificate probes for client domains.
lib.rsPostgreSQL pool, migrations and repositories. All SQL lives here.
messaging/campaigns.rsMass mail campaigns: segment selection, recipient snapshot, batched sending state and unsubscribe. Opted-out customers are excluded when the campaign is queued and re-checked right before each send.
messaging/email_log.rsSent-mail log (WHMCS "Email Message Log" / client "Email History").
messaging/email_templates.rsOperator overrides for email templates. Defaults live in locale files (email.<name>.subject / email.<name>.body).
messaging/event_mail.rsLookups behind event-driven customer emails (pelty_api::event_mail).
messaging/link_tracking.rsLink tracking: short /go/{slug} links that redirect and count clicks.
messaging/mod.rsMessaging repositories: email templates and log, event mail, campaigns, link tracking and outgoing webhooks.
messaging/plugin_webhooks.rsPlugin webhook endpoints (secret + URL override) and the delivery log.
messaging/webhook_endpoints.rsOutgoing webhook endpoints (admin-managed) and their delivery log.
ops/admin_ops.rsAdmin ops: dashboard widget/quick-link prefs, login history, staff online, calendar, per-client email log (from the mail job queue), DB backups.
ops/admin_tools.rsAdmin tools: global search, system health, My Notes, notification rules.
ops/audit.rsAudit log repository: record and list admin/client actions.
ops/csv_import.rsCSV import of clients, services and invoices with column mapping, preview and a real dry-run (the whole import runs in a transaction that is rolled back). A live run commits only when every row is valid.
ops/dashboard.rsDashboard aggregates: each figure is one indexed aggregate query, never a per-row loop.
ops/exports.rsTax report and accounting exports. All money stays Decimal; credit notes count negative so a period nets out correctly.
ops/housekeeping.rsSystem cleanup tasks from the WHMCS cron: prune old ticket attachments and keep customer active/inactive status in sync with what they own.
ops/job_stats.rsRead-only aggregates over the job queue for the automation dashboard.
ops/jobs.rsPostgres job queue: FOR UPDATE SKIP LOCKED claiming, exponential backoff retries, idempotency keys and stuck-job recovery.
ops/mass.rsRow updates behind admin list mass actions (customers, domains).
ops/mod.rsAdmin operations repositories: audit log, dashboard, reports, exports/imports, mass actions, jobs and housekeeping.
ops/report_library.rsWHMCS-style report library. Every report is a fixed SQL query over a [from, to) time window returning text cells (money stays exact: NUMERIC is rendered by Postgres, never through floats).
ops/reports.rsMonthly financial reports. Every figure is computed in SQL with NUMERIC (exact decimals); months are UTC calendar months.
registry/domain_defaults.rsDefault nameservers and DNS template for new domain orders (WHMCS "Default Nameservers" and DNS templates).
registry/domain_pricing.rsRegistrar TLD cost sync with margin rules, premium-name pricing and per-TLD additional order fields. All money is exact Decimal.
registry/domains.rsDomains: TLD price list, orders (register/transfer), renewals and expiry reminders. Registrar calls happen in the API worker; this module only holds state. Prices are exact decimals.
registry/domains_service.rsDomainService: Registered domains and their lifecycle.
registry/mod.rsDomain repositories: registered domains, TLD pricing, defaults and registrant verification.
registry/registrant_verify.rsICANN registrant e-mail verification (WHOIS accuracy).

#pelty-docs

Documentation generator for PeltyCMS. Everything it writes is derived from code: the route inventory and utoipa schema, the event registry, the WASM runtime's capability/hook types, the WIT world, the UI schema, the config loader, the migrations directory and the extensions/examples/addon-guide sources. Guide prose lives in templates/.

ModuleWhat it does
addons.rsOne page per bundled addon, generated from its addon.json plus the setting/field declarations found in its Rust source, and a catalog page.
coverage.rsMachine-checked documentation coverage. Each category lists the items the code defines and checks that the page documenting them mentions every one. tests::nothing_is_missing fails when an item is missing.
doc.rsTiny document model rendered to HTML and Markdown, plus a parser for the Markdown subset used by templates/ (headings, paragraphs, - lists, fenced code, tables via directives).
extract.rsSource extractors: struct fields with doc comments, trait/fn source, marked snippets, env vars and migrations.
highlight.rsSyntax highlighting at generation time (syntect with the extra grammars from two-face). Output uses hl-<scope atom> classes; the colours (GitHub dark) live in templates/style.css, so pages need no JavaScript to look right.
lib.rsDocumentation generator for PeltyCMS. Everything it writes is derived from code: the route inventory and utoipa schema, the event registry, the WASM runtime's capability/hook types, the WIT world, the UI schema, the config loader, the migrations directory and the extensions/examples/addon-guide sources. Guide prose lives in templates/.
main.rspelty-docs [OUT_DIR], writes the developer docs (HTML, Markdown, JSON) to OUT_DIR (default target/docs). Run by cargo xtask docs (or make docs).
pages.rsBuilds every page. Reference pages are assembled from code; guide pages come from templates/*.md with directives that pull code in.
platform.rsPlatform reference pages built from structured sources: system settings keys, general settings, email templates and scheduled tasks.
reference.rsEvents index, "cancel and modify" reference, widgets/slots index and hook-points index. Everything here is derived from the event registry, the emit sites in the source, the router and the frontend.
routes.rsRoute inventory parsed from crates/pelty-api/src: every .route("…", …) call outside #[cfg(test)], with its methods, handlers and mount prefix. full_spec merges it with the utoipa document so every registered route appears in the API reference, documented or not.
slots.rsAddon UI injection points discovered from the frontend source: every page under the admin and client layouts (frontend/src/App.tsx), its route key and the slot names the Shell renders for it, plus the named slots placed by hand (<PluginSlot slot="…">, <PluginWidgets slot="…">).
userdocs.rsThe general documentation set at /docs/: a landing page and the Administrator and Client guides. Prose lives in templates/user/*.md; tables are pulled from code with directives (see expand).

#pelty-extensions

Themes and plugins: manifest loading/validation, hook bus, registry.

ModuleWhat it does
addon_pkg.rsAddon packages (.zip): pack, inspect and safely extract.
addon_signing.rsDetached Ed25519 release signatures. Trust comes only from operator keys.
events.rsDomain events published by core modules and consumed by plugins and addons. REGISTRY is the single source of truth: the generated docs (make docs) render it, and a test fails when the code emits an event (or a payload key) that is not registered here.
filters.rsFilter points: values the host passes through addon filters before using them (WordPress-style apply_filters). Each listener gets {filter, value, context} and replies {value}; the host keeps the JSON schema of the value (see same_schema), applies the money rules and always fails open (a failing filter leaves the value unchanged).
functions.rsCore functions addons can wrap with mixins (capabilities.mixins with target fn <name>). A before/around mixin may rewrite the function's input, an after/around mixin its output; both must keep the JSON schema (see same_schema) and never change money values. Identity values (who, which record) are passed in a read-only context. Mixins fail open: a failing or invalid reply leaves the value unchanged.
lib.rsThemes and plugins: manifest loading/validation, hook bus, registry.
plugins.rsPlugin model. Built-in plugins implement Plugin and are compiled in. External plugins are declared by extensions/plugins/<id>/plugin.json and run out-of-process (signed HTTP webhooks, see make docs). No native dynamic library loading, ever.
themes.rsThemes: extensions/themes/<id>/theme.json with whitelisted design tokens and optional static assets. No JS. Token values are validated against a strict character set so they cannot break out of a CSS declaration.
ui_schema.rsDeclarative plugin UI: plugins describe pages and slot contributions as a JSON node tree that the frontend renders natively with shadcn components and the active theme. No plugin JavaScript ever runs in the app; embed (a sandboxed iframe) is a documented last resort behind the ui.embed capability. See make docs for the node reference.

#pelty-import

WHMCS / Blesta / Paymenter -> PeltyCMS importer.

ModuleWhat it does
blesta.rsBlesta -> PeltyCMS. Reads a JSON export whose top-level keys are Blesta table names (clients, contacts, packages, pricings, ...) holding arrays of rows with Blesta's column names, and converts it to the WHMCS-shaped model consumed by crate::import::run_source.
foreign.rsShared helpers for converting other billing systems (Blesta, Paymenter) into the WHMCS-shaped WhmcsData model, so a single, tested writer (import::run_source) does all the inserts.
import.rsWrites a WhmcsData snapshot into PeltyCMS.
import_more.rsWHMCS servers, contacts, promotions and tax rules (same transaction and import_map idempotency as the rest of crate::import).
lib.rsWHMCS / Blesta / Paymenter -> PeltyCMS importer.
map.rsPure WHMCS -> PeltyCMS mapping rules.
paymenter.rsPaymenter (v1, Laravel) -> PeltyCMS. Reads a JSON export whose top-level keys are Paymenter table names (users, categories, products, plans, prices, services, invoices, invoice_items, invoice_transactions, gateways, tickets, ticket_messages, credits) and converts it to the WHMCS-shaped model consumed by crate::import::run_source.
sqldump.rsReads a WHMCS database from a plain SQL export (mysqldump whmcs > whmcs.sql, phpMyAdmin "Export > SQL", or a hosting panel backup), so an import needs no running MySQL. Only CREATE TABLE (for column order) and INSERT|REPLACE [IGNORE] INTO ... VALUES (...), (...) statements are read; everything else (SET, LOCK TABLES, comments, indexes) is skipped. Values keep MySQL's text form; NULL is None. --hex-blob values (0x...) are decoded to text.
whmcs.rsRow structs for the subset of the WHMCS schema the importer reads. Column names follow the WHMCS database schema (tblclients, tblcurrencies, tblproductgroups, tblproducts, tblpricing, tblhosting, tblinvoices, tblinvoiceitems, tblaccounts). Money columns are decimal(16,2) in WHMCS and are carried as exact Decimal, never floats.
whmcs_crypt.rsWHMCS secrets.

#pelty-notifications

Outbound email: a tiny template renderer, the Mailer trait and its SMTP / log / in-memory implementations. Delivery runs through the job queue (see pelty-api::worker), never inline in a request.

ModuleWhat it does
lib.rsOutbound email: a tiny template renderer, the Mailer trait and its SMTP / log / in-memory implementations. Delivery runs through the job queue (see pelty-api::worker), never inline in a request.
mailer.rsSMTP mailer built on lettre: builds and sends multipart emails.
template.rs{{var}} substitution. Deliberately logic-less: operators edit templates in the admin UI, so there is nothing to execute.

#pelty-payments

Payment gateway abstraction. Gateways are drop-in modules implementing PaymentGateway and registered in GatewayRegistry. All provider HTTP goes through http::HttpTransport so it can be mocked.

ModuleWhat it does
http.rsHTTP transport used by every gateway. Gateways build HttpRequestSpecs and send them through an HttpTransport, so request logic is testable without network (in-memory MockTransport) or against a local mock HTTP server through ReqwestTransport plus the gateway's api_base override.
lib.rsPayment gateway abstraction. Gateways are drop-in modules implementing PaymentGateway and registered in GatewayRegistry. All provider HTTP goes through http::HttpTransport so it can be mocked.
wire.rsJSON wire format between the host and WASM gateway addons (pelty:[email protected] world, kind gateway). Money travels as exact decimal strings, never floats.

#pelty-provisioning

Provisioning and registrar contracts: the module traits, registries, HTTP transports and shared helpers. No panel code lives here: every module (Pterodactyl, cPanel, Proxmox, ...) is its own addon crate under extensions/addons/prov-*, loaded as WASM. Scheduling, retries and persistence live in the job queue (pelty-api::worker). Every module describes its server and product settings with a declarative field schema, so the admin UI renders them natively without module code.

ModuleWhat it does
http.rsSmall JSON HTTP helper shared by modules: bearer auth, timeouts, error classification (4xx = rejected, 5xx/network = transient). Never logs keys.
lib.rsProvisioning and registrar contracts: the module traits, registries, HTTP transports and shared helpers. No panel code lives here: every module (Pterodactyl, cPanel, Proxmox, ...) is its own addon crate under extensions/addons/prov-*, loaded as WASM. Scheduling, retries and persistence live in the job queue (pelty-api::worker). Every module describes its server and product settings with a declarative field schema, so the admin UI renders them natively without module code.
licensing.rsSoftware licensing primitives (the licensing addon is the module) (WHMCS Licensing Addon style). No remote panel: create mints a license key, lifecycle actions are reflected by the service status, and the public verify endpoint (POST /api/v1/licensing/verify) answers with a response signed with HMAC-SHA256 under the assigned server's API key (the "secret"). The first check binds the license to the caller's domain / IP / directory (if the product allows binding); reissue clears the binding.
registrar.rsDomain registrar modules. Real registrars implement RegistrarModule; MockRegistrar is an in-memory implementation for tests and demos and never touches the network.
registrars.rsRegistrar host plumbing: the HTTP transports registrar addons send through, and the RDAP lookup used by the domain search. The registrars themselves (Namecheap, ResellerClub, Enom, OpenSRS, GoDaddy, CentralNic, generic JSON) live in extensions/addons/reg-*.
sso.rsClient single sign-on into a provisioned panel.
template.rsTiny {{name}} template renderer used by the webhook and agent modules. No logic, no loops: unknown placeholders render empty, so a template can never reach data it was not given. Values are escaped for their target (JSON string content, URL path segment or a shell-free plain string).
util.rsShared helpers for the panel / cloud modules.
wire.rsJSON wire format between the host and WASM addons of kind provisioning and registrar (pelty:[email protected]). Shared by the guest SDK and the host adapters so both sides agree byte for byte.

#pelty-saml

SAML 2.0 Service Provider primitives, pure Rust, no unsafe, no C.

ModuleWhat it does
c14n.rsExclusive XML Canonicalization 1.0 (without comments), <https://www.w3.org/TR/xml-exc-c14n/>, applied to an element subtree.
dsig.rsXML-DSig verification for enveloped signatures, restricted to a safe profile: one Reference to the parent element by ID, Exclusive C14N 1.0, SHA-256 digest, RSA-SHA256 or ECDSA-P256-SHA256. The key always comes from the configured IdP certificate; KeyInfo in the message is ignored.
keys.rsSigning keys and self-signed certificates (SP request signing, and the simulated IdP used by tests).
lib.rsSAML 2.0 Service Provider primitives, pure Rust, no unsafe, no C.
sp.rsSAML 2.0 Web Browser SSO profile, Service Provider side.
testing.rsA simulated Identity Provider for tests and the admin "test" button fixtures: builds and signs SAML Responses with locally generated keys. Not used on any production code path.
xml.rsMinimal namespace-aware XML tree built on quick-xml.

#pelty-server

PeltyCMS server binary.

ModuleWhat it does
bootstrap/mod.rsFirst-run bootstrap (Minecraft-server style): when pelty-server starts in a folder it writes a commented .env (with a freshly generated encryption key), creates the folders the server writes to and the default locale files. Idempotent: existing files are never overwritten; later starts only create what is missing.
console.rsInteractive server console (Minecraft-server style): a line reader on stdin next to the HTTP server. On a terminal it uses a line editor (history, editing) whose external printer carries the log lines, so logs never garble the prompt; with --console on a pipe it reads plain lines. Commands reuse the same service functions as the admin API / CLI.
embedded.rsFiles compiled into the binary by the embed-frontend feature (see build.rs): the web UI and the default themes, plus the addon zips only with the opt-in embed-addons feature (off by default and in releases). The UI is served straight from memory; themes and addon zips are written to disk because the server (and the admin, for themes) works with folders.
eula.rseula.txt gate (Minecraft-server style). The first start writes eula.txt with eula=false; serve refuses to start until it says eula=true or the environment sets PELTY_EULA=true (Docker / CI).
logging.rsTracing setup. Log lines go through Sink: straight to stdout, or, //! while the interactive console runs, through the line editor's external printer so they never garble the prompt. The filter sits behind a reload handle so log level <filter> can change it at runtime.
main.rsPeltyCMS server binary.
setup/ai.rsOptional provider configuration; secrets are collected without echoing.
setup/mod.rsInteractive first-run setup wizard (pelty-server setup, or automatically when serve starts in a terminal while .env says PELTY_SETUP=pending).
setup/term.rsTerminal prompts (dialoguer: hidden input works on Windows and Unix) and the live database / SMTP checks used by the wizard.
setup/tests.rsTests for the interactive first-run setup with scripted answers.
update.rspelty-server update [--check] [--version X] [--yes] [--no-backup] and the console update: install a release over the running binary.
whmcs_import.rsimport whmcs for the CLI and the console: read a WHMCS SQL export (mysqldump / phpMyAdmin, no MySQL server needed) or a live MySQL DSN, decrypt server passwords with the WHMCS cc_encryption_hash, seal them with PELTY_ENCRYPTION_KEY and run the idempotent importer.

#pelty-wasm

Sandboxed WebAssembly addons (Component Model / WIT).

ModuleWhat it does
gateway.rsHost adapter: a WASM addon of kind gateway exposed as a PaymentGateway.
lib.rsSandboxed WebAssembly addons (Component Model / WIT).
provisioning.rsHost adapters: WASM addons of kind provisioning / registrar exposed as ProvisioningModule / RegistrarModule.
runtime.rsGeneric addon runtime for WIT world pelty:addon/[email protected].