Start your first Cedros extension with one small feature, a complete package, and a test installation. The minimal example in the authoring kit gives you an admin page and the files needed to build an uploadable ZIP.
An extension is appropriate when you need functionality beyond a page's content or a theme's styling. If you are still choosing an approach, see Choosing between a custom page, theme, and extension.
Get the kit and a test site
Download the extension authoring kit and unpack it. Read AUTHORING-KIT.json for the kit version, supported platform API, and compatibility details. The public extension authoring guide provides the same reference material online.
The current example uses Node.js 24.17.0 and npm 11.13.0. Check your versions with node --version and npm --version; use the example's version files and package metadata when setting up your development environment.
Use a separate development or test site where you have permission to manage extensions. Confirm that it accepts private ZIP uploads before planning an installation. Local package upload and server-runtime availability depend on the host; access to an admin account alone does not enable them.
For your first pass, keep the feature small: an admin page that displays a short message is enough to prove the build, package, and loading workflow. Add storage, providers, payments, or background jobs after that foundation works.
Copy the minimal example and set its identity
Inside the unpacked kit, copy docs/extension-authoring/examples/minimal-extension into your own project folder. Open a terminal in that copied folder. Preserve the example's scripts and package structure while you customize it.
The main files and folders are:
cedros-extension.manifest.json: the canonical extension identity, compatibility, package family, declared surfaces, and admin navigation.packages/react: the browser package and admin registration. The example page is insrc/admin/register.tswithin this package.packages/server: the server registration crate and its Cargo package identity.packages/react-native: the companion package included in the example's family. Keep its identity and version aligned; its presence does not establish that a target site supports a mobile feature.scripts: manifest synchronization, validation, tests, and ZIP packaging.
Replace the demo identity consistently before distributing your extension. Set a unique, stable extensionId, a display name, and a release version. Align the package-family names and versions with the React package manifests and packages/server/Cargo.toml. Update registration entrypoints, admin module IDs, section IDs, and navigation references to match.
In the Cargo package, rename [package].name and leave [lib].name omitted unless you have a specific reason to set it. Cargo's derived crate name replaces hyphens with underscores; the manifest's server entrypoint must use that same crate name.
Run npm run sync:manifest to regenerate the bootstrap and package-local manifest projections from the canonical manifest. Do not maintain those generated copies by hand. The kit's 05 · Package Family and Registration reference explains the alignment rules.
Build and package the first version
For a copied example whose npm package names changed, run these commands in order from its project folder:
npm install --package-lock-only --ignore-scripts --audit=false --fund=falseto refresh the lockfile after the renames.npm cito install the locked dependencies.npm run packageto synchronize the manifest, build the packages, run validation and the scaffold tests, and write the ZIP underdist/.
The package command includes its build and validation steps. For a separate validation pass after a build, use npm run validate; on a fresh source copy, validation can report missing compiled files until you build them.
Change the minimal page's message or other small behavior in its admin registration source, rebuild, and package again. Keep manifest navigation and runtime registration consistent. A declaration that says a page exists does not replace the component that renders it.
Use the kit's deterministic verifier as an additional check on the final source and ZIP. Its script is docs/extension-authoring/scripts/cedros-authoring.mjs inside the kit; 22 · Packaging and Upload and the example README describe the artifact requirements. Resolve reported failures and distinguish a blocked check from a pass.
The ZIP must contain the canonical manifest and matching bootstrap, plus the compiled files needed by its declared features. For an admin module, the React package's ./admin/register export must resolve to JavaScript included in the archive. Keep exactly one copy of each canonical manifest filename and one matching React package identity.
Do not substitute a source-code ZIP for the packaged result. The installing host does not fetch npm dependencies or build missing runtime files for you. Keep credentials, local settings, dependency caches, and build caches out of the archive.
Add a server backend only when needed
A browser-only admin page does not need a Wasm backend just to display its content. If your feature needs server routes, durable data, privileged provider calls, or background jobs, read the kit's 07 · Server Backends reference and start from its extension-with-server-wasm example.
Before building that backend, check the target host's authenticated GET /admin/runtime/wasm-extensions/capabilities response. Confirm that Wasm extensions are enabled and that the host's WIT interface version and checksum match your build contract. WIT defines the interface between your component and Cedros.
The supported backend is a Rust component built for wasm32-wasip2. Vendor the kit's WIT contract unchanged, declare the capabilities you use, and include the compiled cedros-extension.server.wasm in the ZIP. The server example's package scripts perform its component build; it also requires the appropriate Rust toolchain and target.
Declared routes and jobs must match the component's exported registrations. Declaring them in the manifest without implementing and packaging them is not sufficient.
Keep secrets and privileged operations on the server. Use Cedros's documented identity, permission, storage, settings, and provider contracts. A hidden button is not authorization: a private route must check the host-supplied identity and required permissions, and mutations must follow the host's CSRF requirements. Test denied access and missing dependencies as well as successful requests.
Install the exact ZIP and verify it
- On your test site, open Extensions → Upload extension, select the ZIP from
dist/, and choose Upload package. - Review the extension's identity, version, added features, requested access, and compatibility results. Resolve blockers before proceeding.
- Choose Install without enabling if review or setup is still pending, or Install and enable when you are ready to run it.
- Confirm the installed version and enabled state. Open the actual admin page and verify your message or feature, rather than stopping at a successful installation notice.
- Exercise the feature's failure cases and any server route or job you added. Confirm the expected result in the destination system where appropriate.
See Uploading a private extension for the owner-facing upload and review workflow.
If you see Admin bundle not loaded, inspect the packaged JavaScript, registration exports, extension ID, version, and admin navigation alignment. It is a fallback message, not evidence that your component ran. If the host rejects the upload, use the reported archive or compatibility error to fix the package before retrying.
For MCP-driven installation, discover the target site's current extension tools and their schemas first. Uploading, installing, and enabling are consequential operations; target the reviewed artifact and site explicitly. See MCP permissions and publishing controls.
Prepare the next release
Keep stable IDs for the same extension and its existing contributions. Increase the release version and align it across the manifest, package family, and runtime registrations, then regenerate and validate the package.
Test an update from the previous installed version, not only a clean install. Check that disable stops the feature, re-enable restores it appropriately, and updates preserve the data your users expect to keep. Define what uninstall retains or removes, and make that choice clear to the site owner.
Retain the exact tested ZIP, its checksum, compatibility requirements, setup instructions, and test results. A successful build proves that an artifact was produced; the installation and feature checks establish whether it works on the target host.
Use Updating, disabling, or removing an extension for the operator lifecycle. Review production deployment separately, including recovery instructions: reinstalling an older version does not automatically reverse a data migration.