Skip to content

0005 — Extensibility tiers: core, core plugins, community plugins

  • Status: Accepted
  • Date: 2026-08-14

Context

The boundary between "application feature" and "plugin" has to be drawn before features are written, not after. Drawn late, it is drawn by accident: whatever happened to be built first becomes core, the plugin API is retrofitted to whatever core happens to expose, and it is discovered to be inadequate only when the first external author tries to use it.

Obsidian's three-tier structure is the model worth copying, and the reason it works is not the tiers themselves — it is that first-party bundled features are built on the same public API that community authors get.

Decision

Three tiers, with a hard rule attached to the middle one.

Tier 1 — Core

Non-negotiable. No plugin may replace it, and it is the substrate everything else is written against.

AreaResponsibility
Editor hostThe CodeMirror instance, its extension registry, the buffer/document lifecycle
Document & file I/OReading, writing, atomic saves, encoding detection, external-change reconciliation
Project modelWhat a project is, its root, its file tree, its build entry points
Compile orchestrationEngine abstraction, invocation, log parsing, artefact management (0007)
PDF hostThe viewer surface and page lifecycle
Plugin runtimeLoading, lifecycle, capability broker, permission grants (0006)
Settings storeSchema, persistence, migration, the settings UI host
Theme engineCSS custom property contract, theme loading and hot reload (0010)
i18n runtimeCatalogue loading, message resolution, locale fallback (0011)
Command registryNamed commands, the palette, and the keymap layer including the Vim host
WorkspacePane layout, tabs, split views, view registration
UpdaterApplication and plugin updates (0013)

The test for core membership: would the application be incoherent without it, or does more than one plugin need to depend on it?

Tier 2 — Core plugins

Bundled with the application, enabled by default or opt-in, maintained by us, individually toggleable — and written strictly against the public plugin API, with no privileged access whatsoever.

Core pluginNotes
vimVim keybindings, on the core keymap API
zoteroLibrary search, citation insertion, .bib sync (0008)
obsidianVault browsing, Markdown → LaTeX (0009)
bibliography.bib management, deduplication, key hygiene
outlineDocument structure navigator
templatesImport and manage journal/conference templates
gitVersion history, diff, commit
synctexForward and inverse search between source and PDF
spellcheckDictionaries and LanguageTool integration
tablesTable editing affordances in visual mode
figuresAsset import, placement, and management
exportDOCX/HTML/Markdown export
snippetsSymbol palette, snippet expansion
statsWord count, readability, progress tracking

The rule: a core plugin that needs an API which does not exist yet gets the API added to core as public API — never a private back door. This is the entire point of the tier. Shipping the Zotero bridge and Vim as core plugins means the plugin API is exercised by demanding, real features from month one, and its inadequacies surface to us before they surface to an external author.

Tier 3 — Community plugins

Third-party, installed from a GitHub release or from a local file, listed in a separate yaz-releases index repository. Identical API to tier 2.

Consequences

  • The plugin API cannot quietly become a second-class citizen, because our own headline features stop working if it does.
  • Users can disable substantial parts of the application. A user who does not use Zotero pays nothing for it — no memory, no startup cost — which serves the performance goals directly.
  • Core plugins must be lazily loaded and must not be assumed present by other code, since any of them may be off.
  • We are constrained in a way that costs real time: when a core plugin needs something, we must design a general API rather than the narrow thing that would have unblocked us. This is the price of the tier and we pay it deliberately.
  • Moving a feature between tiers later is a breaking change for anyone depending on it, so the initial placement above matters and changes to it need an ADR.
  • The core surface is large. We limit growth with the stated membership test, and treat any proposal to add to tier 1 as requiring justification against it.

Alternatives considered

Two tiers — core application plus community plugins. Simpler, and the path of least resistance. Rejected because it removes the forcing function entirely: first-party features would be written against internals, and the plugin API would be whatever we imagined external authors might want, validated by nobody.

Everything a plugin, with a minimal kernel. Architecturally elegant and genuinely tempting. Rejected because a text editor, a file model, and a command system are not optional; making them replaceable buys configurability nobody asked for at the cost of an unstable foundation for every plugin.

Core plugins with privileged internal access. The pragmatic compromise — bundled features get shortcuts, external ones do not. Rejected precisely because it is the failure mode this ADR exists to prevent.

Application AGPL-3.0-or-later · @yaz/api MIT · docs CC BY-SA 4.0