The SaaS-replacement platform Shared system for teams and agents. Official CLI + GraphQL surface.

Build bounded extensions

Bounded extensions move through an explicit build, validation, and promotion lifecycle.

Bounded extension building keeps focused capability on the shared base. The default path is intentionally simple: keep the extension workspace-scoped, standard-risk, and bundle-first; use the CLI to lint the source tree, verify it in a sandbox workspace, and only then install, validate, activate, monitor, and upgrade it; only move into a service-backed runtime when the requirement truly needs custom handlers, event consumers, scheduled jobs, or an owned `ext_*` PostgreSQL schema. Under the current public license and distribution policy, teams can keep custom extensions private for use inside their own company or give them away for free; selling extensions, derivative works, or hosted access requires separate written permission.

Agent-first route: Start at /agents, inspect /docs/cli, and drop into this page when you need proof, detail, or rollout guidance.

Table of contents

Section map.

Jump directly to the part you need.

Before you start

Do not start a custom extension repo before the runtime and preview path exist.

The extension SDK is not the first step in the overall self-hosted journey. It assumes there is already a Move Big Rocks instance you control, that `mbr` can authenticate to it, and that a preview workspace exists for safe install and validation work.

  • If you do not yet have a running instance, use `/docs/self-host` first.
  • Install and authenticate the CLI before trying the extension loop.
  • Use a preview or sandbox workspace for the first install and verification cycle.
  • Create a separate custom extension repo only when the team is building real new logic rather than changing instance config or branding.

Default build rule

Start with the smallest safe extension shape.

The extension SDK and security model are opinionated on purpose. Most custom extensions should begin as workspace-scoped, standard-risk, bundle-first extensions. That keeps the lifecycle legible to agents and keeps the blast radius small while the workflow is still proving itself, whether the finished extension stays private or is later given away as a free public bundle.

Proof bundle

  • Keep the first version `workspace` scoped.
  • Keep the first version `standard` risk.
  • Keep the first version `product` or `operational` kind.
  • Keep the first version `bundle` runtime.
  • Make workspace-scoped admin UI work cleanly for instance admins without a live workspace session.
  • Do not force `instance`, `privileged`, `identity`, or `connector` extensions through the generic self-built path.
  • Do not start with service-backed infrastructure unless the requirement clearly needs it.

CLI lifecycle

The CLI is how humans and agents make the extension concrete.

The public build story is intentionally command-driven. The CLI makes the extension lifecycle visible: an extension can be linted from source, verified in a sandbox workspace, and then installed, activated, monitored, upgraded, and deactivated through one stable machine contract.

Proof bundle

  • Contract docs/AGENT_CLI.md Defines the supported agent CLI contract, auth modes, and command surface.
  • Contract docs/swagger.yaml Documents the public API surface, auth model, and health endpoints.
  • Implementation cmd/api/routers.go Implements the split between `/graphql`, `/admin/graphql`, public routes, and authenticated agent surfaces.
  • Enforcement .github/workflows/_test.yml Verifies builds, tests, and contract-sensitive paths on the public platform codebase.
  • Check `extension.contract.json` into the repo and refresh it only when the declared extension surface changes intentionally.
  • If the extension exposes workspace-scoped admin UI, verify that an instance admin without an active workspace still sees a working entrypoint.

Recommended contract-first loop

This is the shortest path from private extension repo to a live preview-workspace installation.

$ mbr extensions lint . --json$ mbr auth login --url https://app.yourdomain.com$ mbr workspaces list$ mbr extensions verify . --workspace ws_preview --json$ mbr extensions nav --instance --json$ mbr extensions widgets --instance --json$ mbr extensions skills list --id EXTENSION_ID --json

Upgrade or roll back the same extension

The same explicit lifecycle continues after the first install.

$ mbr extensions lint . --write-contract --json$ mbr extensions verify . --workspace ws_preview --json$ mbr extensions nav --instance --json$ mbr extensions widgets --instance --json$ mbr extensions deactivate --id EXTENSION_ID --reason "rollback" --json

Public publication

The SDK includes the same build, sign, and OCI publication tooling used for the public first-party bundles.

A custom extension can stay private, but if you want to give it away for free the SDK now gives you a concrete publication path instead of leaving that step vague.

  • Put the generated seed into your publishing environment as `MBR_EXTENSION_SIGNING_PRIVATE_KEY_B64`.
  • Put the generated trusted publisher JSON into your instance config as `EXTENSION_TRUSTED_PUBLISHERS_JSON`.
  • Public signed bundles do not need an instance-bound token claim.
  • Controlled private bundle flows can still add `--instance-id` and `--license-token` during signing.
  • Publishing from a public repo keeps the GHCR package surface public and legible.

Build, sign, and publish a free public bundle

This follows the same shape as the first-party public bundle workflow.

$ go run ./scripts/generate-signing-key.go --publisher DemandOps --key-id demandops-public-1 --seed-out secrets/demandops-public-1.seed.b64 --trusted-publishers-out dist/demandops-public-1.publisher.json$ go run ./scripts/build-bundle.go --source . --out dist/my-extension.bundle.json$ go run ./scripts/sign-bundle.go --bundle dist/my-extension.bundle.json --out dist/my-extension.signed.bundle.json --key-id demandops-public-1 --private-key-env MBR_EXTENSION_SIGNING_PRIVATE_KEY_B64$ ./scripts/publish-bundle-oci.sh --bundle dist/my-extension.signed.bundle.json --image ghcr.io/movebigrocks/mbr-ext-my-extension --tag v0.1.0

When to go service-backed

Move to a service-backed extension only when the requirement truly needs more than a bundle.

The service-backed runtime is valuable, but it exists for concrete technical reasons rather than status. Use it when the extension needs its own handlers, jobs, consumers, or data model; otherwise keep the extension bundle-first. The SDK now treats this as an explicit upgrade step rather than something authors must invent from scratch.

Proof bundle

Custom handlers

Use `service_backed` when the extension needs server-side endpoint handlers beyond asset-backed public or admin pages.

Event consumers and jobs

Use `service_backed` when the extension needs long-running consumers, scheduled jobs, or richer background behavior.

Owned PostgreSQL schema

Use `service_backed` when the extension needs its own `ext_*` PostgreSQL schema, migration history, and bounded data model.

Same event and outbox rules

Even then, the extension should publish and consume typed events through the same outbox and event-bus pattern as core.

Proof

Promotion sequence

The build sequence keeps the extension legible to both humans and agents.

A bounded extension lifecycle is one of the clearest ways to keep custom software safe and understandable.

  • Define the capability slice and the core primitives it will reuse.
  • Implement the package and its explicit runtime surfaces.
  • Run the threat model and review checklist before activation.
  • Deploy and validate it in a preview workspace.
  • Decide explicitly whether the finished extension stays private or is given away as a signed public bundle.
  • Review scope, permissions, runtime health, and operational impact before production activation.

References

Canonical next surfaces.

Each link goes to the next authoritative page, reference, or support surface.

Move Big Rocks

Let agents inspect the CLI-first surface. Let humans decide trust, rollout, and boundaries.

Start from /agents, use /docs/cli as the official product tour, inspect /resources for source and proof, and review /security before making deployment or data-handling decisions.