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/Guides

Security model

#Capabilities

Capabilities approved at install (from addon.json).

FieldTypeRequiredDescription
httpVec<String>noAllowed hosts: api.stripe.com, *.example.com, $server, $setting:<key>.
secretsVec<String>no
kvboolno
eventsVec<String>no
hooksVec<Hook>noSynchronous pre-action hooks (invoice.before_create, …).
routesVec<Route>noHTTP routes served under /api/v1/addons/<id>/….
uiVec<String>noUI powers beyond the declarative schema: iframe = sandboxed pages and widgets served from the addon's assets/ (admin-approved).
bridgeVec<String>noCore GET /api/v1/… prefixes an iframe may read through the postMessage bridge (with the viewer's own permissions).
http_privateboolnohttp: ["*"] may also reach loopback/private/LAN addresses. Link-local and cloud metadata stay refused.
moneyMoneyCapsnoChange money amounts in pre-event payloads (audited, exact Decimal).
dbDbCapsnoCore data access through the host (power interface).
fsboolnoPrivate data directory, mounted at /data (WASI preopen).
execVec<String>noAbsolute program paths the addon may run (no shell). Off in Docker.
exec_argsHashMap<String, Vec<String>>noOptional per-program argument allowlist: each argument must equal a pattern or match a prefix* pattern. Programs not listed only get the baseline checks in [validate_exec_args].
fs_quota_mbOption<u32>noSize cap for the /data directory in MiB (default 64, max 1024). Over quota the directory is mounted read-only.
jobsVec<Job>noBackground jobs: ops the addon may queue, and cron schedules.
auth_providerboolnoCustom login: the host asks the addon to verify credentials (op auth.verify). It never sees stored password hashes.
emitsVec<String>noCustom events this addon may emit (<id>:<name>, * suffix allowed).
mixinsVec<Mixin>noHost-enforced wrappers around approved host operations.

Source: crates/pelty-wasm/src/runtime.rs (Capabilities).

#Hook

A pre-event subscription: the host calls call("hook", {event, payload}) before the action and the addon may continue, cancel or modify it.

FieldTypeRequiredDescription
eventStringyes
priorityi32noLower runs first; ties ordered by addon id. Accepts an integer or a Bukkit-style name: lowest (0), low (50), normal (100), high (150), highest (200), monitor (1000, observe only).
ignore_cancelledboolnoAlso receive the event when an earlier listener cancelled it.
fail_openboolnoOn error/timeout: true lets the action continue, false blocks it.
timeout_msu64noHook budget in milliseconds (capped by the sandbox timeout).

Source: crates/pelty-wasm/src/runtime.rs (Hook).

#Mixin

A mixin: wraps an approved host operation (an API route template such as POST /api/v1/client/orders). at: before (rewrite the input), after (rewrite the output within its schema), around (both, with shared state) or return (replace the output, same schema).

FieldTypeRequiredDescription
targetStringyes
atStringyes
priorityi32no
timeout_msu64no

Source: crates/pelty-wasm/src/runtime.rs (Mixin).

#Route

An addon HTTP route. access: admin (needs permission), client (any signed-in user) or public.

FieldTypeRequiredDescription
methodStringyes
pathStringyesPath below the addon prefix, e.g. /stats or /items/:id.
accessStringyes
permissionOption<String>no

Source: crates/pelty-wasm/src/runtime.rs (Route).

#Job

A background job op; every_secs > 0 also runs it on a schedule.

FieldTypeRequiredDescription
opStringyes
every_secsu64no

Source: crates/pelty-wasm/src/runtime.rs (Job).

#MoneyCaps

FieldTypeRequiredDescription
modifyboolno

Source: crates/pelty-wasm/src/runtime.rs (MoneyCaps).

#DbCaps

FieldTypeRequiredDescription
readVec<String>noEntities: clients, services, invoices, orders, tickets, …
writeVec<String>noTyped write ops: client.note, ticket.reply, …
sqlboolnoFree SQL on addon_<id>_* tables.

Source: crates/pelty-wasm/src/runtime.rs (DbCaps).

#Dangerous capabilities

Rust
    /// Capabilities shown with a red warning at install; the admin must tick
    /// each one (`approve` list in the install request).
    pub fn dangerous(&self) -> Vec<&'static str> {
        let mut v = vec![];
        if self.iframe() {
            v.push("ui.iframe");
        }
        if self.iframe_same_origin() {
            v.push("ui.iframe.same_origin");
        }
        if self.http.iter().any(|h| h == "*") {
            v.push("http.any");
        }
        if self.http_private {
            v.push("http.private");
        }
        if self.money.modify {
            v.push("money.modify");
        }
        if !self.db.read.is_empty() {
            v.push("db.read");
        }
        if !self.db.write.is_empty() {
            v.push("db.write");
        }
        if self.db.sql {
            v.push("db.sql");
        }
        if self.fs {
            v.push("fs");
        }
        if !self.exec.is_empty() {
            v.push("exec");
        }
        if self.auth_provider {
            v.push("auth.provider");
        }
        if self.hooks.iter().any(|h| is_request_pattern(split_phase(&h.event).1)) {
            v.push("hooks.any");
        }
        if !self.mixins.is_empty() {
            v.push("mixins");
        }
        if self.mixins.iter().any(|m| m.at == "return" || m.at == "around") {
            v.push("mixins.return");
        }
        v
    }

#Package permissions

Coarse package permissions in addon.json permissions (shown at install):

Addon kinds: provisioning, gateway, registrar, theme, plugin, captcha, sso, integration.