Skip to main content
A plugin system is healthy when “built in” is provenance, not a secret handshake.

1. Motivation

A central branch makes the first provider easy and the fiftieth one political:
Every new package now needs a host source edit and release. Inspection may import implementations, and bundled providers receive privileges an external provider cannot reproduce. HeavenBase uses one Registry-backed module protocol instead. A cohesive folder declares records in meta.yaml; a Context installs and inspects those records; typed facades resolve behavior when needed. Built-in and external items share the same descriptor, Registry, resolver, validation, lifecycle, and contract shape.

2. Separate the Extension Concepts

An Extension is one Registry record kind; it is not the Registry itself. Backends, handlers, logical types, strategies, query operations, serializers, and profiles are peer record kinds rather than fields on a giant Extension object.

3. Understand the Built-In Foundation and Roots

Catalog and MetaSchema are fixed workspace-foundation entities. No Extension can replace them. Capsule is the required Extension root. Toolkit and Prompt are packaged default roots. Agent, Memory, and Database are optional roots that applications enable deliberately. Each Extension declares exact requires identifiers; reopening a workspace recomputes the hard dependency closure from the workspace’s requested roots.
Enablement is monotonic. There is no universal disable/unload operation because only an owning domain can explain how to remove its rows, data, indexes, configuration, and derived effects safely.

4. Declare a Distributable Module Folder

A minimal external entity Extension can use this layout:
The entity remains ordinary public HeavenBase code:
The descriptor publishes the entity and the Extension that activates it:
source: path targets are relative to the module folder. Keep their imports within the captured root or use stable absolute public APIs.

5. Install, Inspect, Resolve, and Enable

install() captures local executable files into a verified content-addressed artifact and returns an exact receipt. inspect() reads inert record data. enable_extension() resolves the Extension and its declared Entity records through the workspace’s Context before registering canonical classes. Use modules.uninstall(receipt) only when you intentionally want to remove that installed generation. Uninstalling a module does not delete application workspace data.

6. Attach a Workspace-Bound API

An Extension may attach one domain service through api and api_name. The factory receives the workspace, so the service inherits the correct Context, canonical Entity classes, and lifecycle.
In a distributable module, publish the factory as a durable heavenbase.executable target in the Extension definition. After enablement, application code uses the attached API, such as ws.notes.list(), while CLI and MCP surfaces remain thin adapters. Programmatic Extension(...).register(resolver=...) remains useful for local or generated definitions. A published package should prefer one meta.yaml so inspection, installation receipts, compatibility, and artifacts remain explicit.

7. Extend Physical Behavior Through Registered Roles

Advanced authors use hb.ext and owning packages for backend types, handlers, strategies, logical types, query operations, Toolkit families, profiles, serializers, and related Registry-backed roles. The rule is the same for every open family:
  1. Publish one canonical record through the target Context’s resolver.
  2. Keep implementation loading behind the typed facade.
  3. Declare exact dependencies and compatibility.
  4. Test built-in/external parity through install, inspect, resolve, use, and uninstall.
Do not add fixed provider scans, privileged import lists, or central planner branches. Do not treat a tag claim as executable proof.

8. Inspect Claims Without Overpromising

Module tags are descriptive declarations and candidate hints. Exact handler records, compiler output, and live resource checks decide whether a concrete query runs natively, through an adapter, by portable scan, or not at all. Use explain() for that execution decision.

9. Use the Extension Checklist

  • Give the module, every record, and every Extension a stable identifier.
  • Keep one exact meta.yaml at the cohesive module root.
  • Declare dependencies instead of relying on import order.
  • Keep path targets inside the captured module root.
  • Attach domain behavior through one workspace API when identity or lifecycle is real.
  • Namespace MCP tools and scope profiles to the exact tools, entities, Skills, and serializer they need.
  • Test a fresh Context restore, inert inspection, activation, ordinary use, and exact-receipt uninstall.
  • Treat installed executable source as trusted code.

Summary

  • Built-in and external modules use one meta.yaml folder protocol.
  • Registry records are inspected before implementations are imported.
  • Workspace Extensions attach domain behavior through explicit roots and dependencies.

Further Exploration

Related resources: