v0.12.0
Released May 11, 2026 · All releases
Plugin unification & hardening. Rune packages are now plugins throughout: site.packages[] becomes site.plugins[], and the old field is rewritten automatically while it is deprecated. The config's JSON Schema is published at a versioned URL, and an opt-in security policy lets a hosted product render untrusted content with layered defences.
Changes
- Rename "rune packages" to "plugins" and unify with CLI plugins. Plugins now contribute runes, layouts, theme config, pipeline hooks, behaviors, and CLI commands through a single npm package.
- Breaking changes:
RunePackageinterface →PluginRunePackageEntry→PluginRuneRunePackageAttribute→PluginAttributeRunePackageThemeConfig→PluginThemeConfigPackagePipelineHooks→PluginPipelineHooksloadRunePackage()→loadPlugin()mergePackages()→mergePlugins()discoverPackageFixtures()→discoverPluginFixtures()LoadedPackage→LoadedPlugin,MergedPackageResult→MergedPluginResultRuneProvenance.packageName→pluginName;source: 'package'→source: 'plugin'RuneInfo.package→RuneInfo.plugin;SerializedRune.package→plugin- Config field
site.packages[]→site.plugins[]. The deprecated top-level shorthandconfig.packages[]is removed; use the existingconfig.plugins[](which now covers both rune contributions and CLI commands). assembleThemeConfiginputs renamed:packageRunes→pluginRunes,packageIcons→pluginIcons,packageBackgrounds→pluginBackgrounds.MergedPluginResult.packages→MergedPluginResult.plugins- CLI:
refrakt package validateremoved; userefrakt plugins validateinstead. - CLI:
refrakt reference list --packageflag is now--plugin(the old name still works as an alias). - Repo layout:
runes/{marketing,docs,…,plan}/workspace globs moved toplugins/{…}/. npm package names (@refrakt-md/marketingetc.) are unchanged. - Migration:
- Rename
RunePackagetoPluginandloadRunePackage/mergePackagestoloadPlugin/mergePluginsin your code. - In
refrakt.config.json, rename per-site"packages": [...]to"plugins": [...]. If you had a top-level"packages"shorthand under flat shape, move it to"plugins". - Replace any calls to
refrakt package validatewithrefrakt plugins validate. - Add an opt-in
SecurityPolicyfor the transform pipeline so hosted products can render untrusted author content with layered defences (WORK-177). - The policy ships with three tiers, each adding a layer on top of the previous one:
- Tier 1 —
'strict': in-package, no JS. The schema transform runs the newsanitizeSandboxContenthelper which strips<script>blocks,on*event handlers,javascript:URLs, and<iframe>/<object>/<embed>tags. The client iframe is built withsandbox="allow-scripts"only (dropsallow-same-origin), and a non-removable visual banner is rendered above the iframe. Only tier with a hard guarantee from the package alone. - Tier 2 —
{ trust: 'untrusted', allowJs: true }: srcdoc + meta-CSP. Author scripts run, but the iframe gets a unique opaque origin and the srcdoc head is prefixed with a meta-CSP that closesconnect-src,form-action,img-src(data + permitted CDN origins only), and gatesscript-src/style-srcto'unsafe-inline'plus the framework preset and declared dependency origins. Closes data exfiltration, off-site form posts, tracking pixels, and external script loads. Does not close fingerprinting, cryptojacking, or browser-exploit chains. - Tier 3 —
{ ..., sandboxOrigin: 'https://sandbox.example.com' }: separate-origin escape hatch. The iframe loads from the host endpoint instead ofsrcdoc, content is delivered viapostMessageafter arf-sandbox-readyhandshake, and the host serves real CSP response headers. Required if you needframe-ancestors/report-uri. Endpoint contract documented insite/content/docs/security/. - The default remains
'trusted'— no behaviour change for self-hosted users. The policy flows throughconfig.variables.__securityPolicyso plugins authoring risky runes can honour it from their own schema transforms; see the new "Honouring the security policy" section insite/content/docs/plugins/authoring.mdfor the contract. - v0.11.0 config follow-ups (WORK-176):
- Schema URL versioning. The JSON Schema is now published at a versioned URL (
https://refrakt.md/schemas/v0.11/refrakt.config.schema.json) with the unversioned URL kept as a "latest" alias.create-refraktscaffolds derive the versioned URL from the package version at scaffold time so old projects don't get false validation errors when later releases add fields. Versioning policy documented insite/content/docs/configuration/schema.md. - Optional mirrored fields.
RefraktConfig.contentDir,theme, andtargetare now typed as optional (?:) — they were strictly required strings before, which papered over the multi-site case where they're undefined. Adapter code that read these directly (refrakt theme install/info,refrakt edit, the Astro/HTML/SvelteKit scaffold templates) now goes throughresolveSite(config).site.contentDirand friends. - Flat-shape deprecation. Loading a flat-shape
refrakt.config.json(top-levelcontentDir/theme/targetwithout asitewrapper) now emits a one-time deprecation warning per process.refrakt config migratementions the v1.0 removal target in its output. Docs (overview.md,migration.md,sites.md,plugins.md) replace flat-shape examples with the nested form and addv0.12 → v1.0deprecation callouts. targetfield downgraded to documentation-only. No adapter actually validates or consumessite.target, soSiteConfig.targetis now optional and the SvelteKit validator no longer requires it on flat-shape configs. The schema markstargetdeprecated: truewith a note that it's slated for removal in v1.0.- Auto-migrate the legacy
packagesconfig field topluginsand emit a one-time deprecation warning. The field was renamed in v0.12.0 when rune packages and CLI plugins were unified, but the parser was silently ignoring the legacy field rather than warning, which broke sites that still usedsites.X.packages. The legacy field now auto-migrates with a console warning and will be removed in v1.0.