Release v0.27 – v0.29

v0.27 – v0.29 Release: Installable Extensions, a Signed Registry, and a New Dashboard

October 17, 2026 · The Smarter Project

v0.24 to v0.26 got Smarter ready to run in public. v0.27 to v0.29, released on October 15 and 16, change how Smarter grows. A new tool for your LLMs used to mean a change to Smarter itself and a new release. Now it is an Extension: a Python package that a platform administrator installs at run time, from a signed registry, without a redeploy. The web console’s dashboard is redesigned around everything the last dozen releases added.

ChangeWhat it means for you
ExtensionsPython packages with their own tools, settings, documentation and lifecycle, installed while the platform runs.
Signed Extension registryBrowse, install and update Extensions from a registry whose index and archives are verified with an Ed25519 signature.
Extensions in the web consoleSuperusers install, configure, enable, update, roll back and uninstall Extensions on two new console pages. Every user sees the Extensions their account may use.
Three official ExtensionsThe weather, calculator and date calculator tools are now Extensions, in their own repositories.
Dashboard redesignA Getting Started card that works, every resource kind in My Resources, and new cards for Extensions, the SDKs, integrations and the Smarter MCP server.
Init jobs you can trustThe init job fails when an official Extension doesn’t install, emails its whole log, and a daily task cleans up orphaned DNS records.

Extensions: tools you install, not tools you release

Smarter could give an LLM new tools in two ways. Plugins are manifests that configure one of Smarter’s built-in tool patterns, such as an API call or a SQL query. And three built-in Python functions, for the weather, a calculator and date arithmetic, were hard-coded into Smarter. Adding a fourth meant changing the platform and shipping a release.

Extensions are the third way, and they work like WordPress plugins. An Extension is a Python package with its own tools, settings, documentation, assets and lifecycle. A platform superuser installs it at run time, from the web console or the registry, and every app, worker and beat pod picks it up without a redeploy.

An Extension describes itself in a standard pyproject.toml. Here is the official weather Extension’s:

[tool.smarter]
slug = "weather"
package = "weather"
display-name = "Weather"
requires-smarter = ">=0.27"
tools = ["weather.tools:GetWeather"]
timeout = 30

[[tool.smarter.settings]]
name = "google_maps_api_key"
type = "secret"
required = true
description = "A Google Maps Geocoding API key, which finds the latitude and longitude of a location."

[[tool.smarter.settings]]
name = "weather_api_url"
type = "url"
default = "https://api.open-meteo.com/v1/forecast"
description = "The Open-Meteo forecast api."

A tool is a class with a name and a Pydantic arguments model. The model’s JSON Schema is the tool’s parameters, and its docstring is the tool’s description, so what the LLM is offered can never disagree with what the tool validates:

class GetWeather(SmarterExtensionTool):
    """Get the current weather, and the forecast for the next 24 hours, for a location."""

    name = "get_weather"
    arguments_model = WeatherArguments

    def call(self, arguments: WeatherArguments, context: ExtensionContext) -> JsonValue:
        settings = context.settings
        place = geocode(arguments.location, settings.google_maps_api_key.get_secret_value())
        ...

An LLMClient lists the Extensions it uses by slug:

spec:
  extensions:
    - weather
  plugins:
    - image_search
  • Typed settings, and secrets that stay secret. Settings are declared in pyproject.toml and configured by the administrator. A secret setting is stored as a Smarter Secret, encrypted at rest, and reaches the tool as a SecretStr.
  • A full lifecycle. Install, update, roll back to any earlier version, configure, enable, disable and uninstall. Hooks can run before and after each step: a pre hook that fails cancels the step, and a post hook that fails leaves the Extension installed but disabled, with the error shown. New Extensions are installed disabled, and one with unset required settings can’t be enabled.
  • Resources that come and go with it. An Extension can ship SAM manifests and waffle switches, which are created on install and removed on uninstall, exactly the ones it created.
  • Updates without restarts. Each version loads beside the previous one, under its own namespace, rather than reloading modules in place, which Python does not do reliably.
  • Failures stay inside the tool. Each call runs with the Extension’s timeout. Invalid arguments, exceptions, timeouts and results that aren’t JSON go back to the LLM as an error, so they don’t fail the prompt, and a broken Extension is skipped and reported.
  • Per-account availability. An Extension is available to every account, or to the accounts you list.
  • Tools named for their Extension. The LLM is offered each tool as ext_<slug>_<tool>, such as ext_weather_get_weather.

Security

Extensions can only import what Smarter already ships. Their dependencies are checked against the platform’s installed libraries, never installed, and an incompatible Extension explains every unmet Python, Smarter or library requirement. An uploaded archive is validated before anything is imported: size, file count and zip bomb limits, and no path traversal or links. An Extension’s documentation is rendered with raw HTML and file inclusion turned off. Only superusers can install, configure or remove an Extension.

📖 Extensibility · Building an Extension


The signed Extension registry

The Smarter Extension registry is an index of published Extensions. Smarter reads it, verifies it, and installs from it:

  • Verified on every read. The index is signed with Ed25519, and Smarter checks the signature with SMARTER_EXTENSION_REGISTRY_PUBLIC_KEY every time it reads the index. An index that doesn’t verify is never cached or used.
  • Verified archives. Every downloaded archive is checked against the sha256 in the signed index.
  • Trust badges. Each publisher is official, verified or community. An archive that you upload yourself is badged unverified, and is never offered a registry update, so a registry package with the same slug can’t replace it.
  • Update checks. A daily task records each Extension’s newest compatible version, and the console shows what can be updated.

The three official Extensions, weather, calculator and date calculator, are published there. initialize_platform installs them, and configures their secret settings from the platform’s Secrets of the same name, such as google_maps_api_key.


Managing Extensions in the web console

Superusers manage Extensions on two new React pages in the web console, under Extensions in the sidebar. A badge counts the Extensions that have an update available.

  • Installed: a table of every installed Extension, with an enable switch per row, an upload button and Check for updates.
  • Browse the registry: search, filter by trust badge, and read an Extension’s documentation before you install it, then install a version from a card.
  • An Extension’s own page has an enable toggle, an update banner, and tabs for its overview, tools, settings, availability, versions (with roll back) and the resources and switches it created. The settings form is generated from the Extension’s pyproject.toml. A secret setting can be kept, typed in, taken from one of the platform’s Secrets, or cleared, and its value is never sent back to the browser.

Everyone else can see which Extensions they may use. The LLM Tools page (formerly Plugins) has a read-only Extensions tab beside the Tool Plugins tabs, with each Extension’s slug, version, publisher, trust badge and whether it’s enabled, so a prompt engineer knows which slugs spec.extensions may list. The page moved from /plugin/ to /llm-tools/, and the old URLs redirect.

📖 Managing Extensions


A dashboard that matches the product

The dashboard had fallen behind. Its widgets were plain lists of links, and none of the recent features (Extensions, the Smarter MCP server, the OpenAPI document, Guardrails, Vectorstores, LLMHosts, Custom Domains) appeared on it.

  • Getting Started works. Two of its steps counted the built-in LLMClients and plugins that every user can see, so every new account started half done, and the card disappeared as soon as a user stored a Secret and deployed an LLMClient. The steps now count only what you own. The card shows until every step is done, then stays for 90 days, and a failed request shows its error.
  • My Resources counts MCPClients, Guardrails, Secrets, Vectorstores, Custom Domains, LLMHosts and Extensions, each linked to its list.
  • Quick Actions adds Guardrails, Apply Manifest, and an API Reference link to your platform’s own OpenAPI page, plus Extensions and Infrastructure for superusers.
  • New cards: Extensions, the SDKs (with the Node.js and Go SDKs and their install commands), integrations (WordPress, Drupal, Shopify, Google Tag Manager, Docusaurus, MkDocs, Sphinx and VitePress), the CLI, the VS Code extension, the free course designs for instructors, the Smarter MCP server with the claude mcp add command for your platform, self-hosting, and contributing.
  • The chat header counts every tool. Smarter Chat’s header, such as openai gpt-4o-mini with 2 additional tools, counted only plugins. The chat configuration now sends a total_tools count that includes Extensions and MCPClients. Guardrails aren’t counted, because an LLM can’t call one.

Behavior changes you will notice

  • spec.functions is now spec.extensions. The LLMClient manifest lists Extensions by slug. The built-in functions get_current_weather, calculator and date_calculator are gone, and their replacements are the official Extensions, whose tools are ext_weather_get_weather, ext_calculator_calculator and ext_date_calculator_date_calculator.
  • API names follow. The LLMClient REST route /llm-clients/{id}/functions is now /extensions, and the chat configuration’s functions list is now extensions. The OpenAPI document, the LLMClient schema and the chat contract are regenerated.
  • An LLMClient whose Extensions aren’t installed is skipped, with a warning, when the built-in LLMClients are deployed, so a registry that can’t be reached doesn’t fail the whole init job.
  • Cloning an LLMClient copies everything it uses. clone() never copied an LLMClient’s plugins, and didn’t copy its MCPClients or guardrails at all. It now copies every link, but only those that the clone’s owner is allowed to use.

Fixes and operations

  • The init job fails when an official Extension doesn’t install, because the built-in LLMClients that need it are then skipped. It used to report success.
  • The whole init job log, by email. The job’s email carried only the last 500 lines of its log, so the cause of an early failure had often scrolled away. It now attaches the whole log, with credentials redacted. A log over 5 MB is uploaded to the platform’s storage under an unguessable name, and linked instead.
  • Orphaned DNS records are cleaned up. A daily task deletes the A records of LLMClient hosts that nothing uses any more, at most 50 a run. manage.py delete_orphan_dns_records --dry-run lists them without deleting anything. It runs only in the AWS environments, and does nothing if it can’t tell which hosts are live.
  • Provider verification survives deploys. Each deploy reset every built-in Provider to verified and active, which re-enabled Providers that had failed verification or that an admin had suspended. Billing, authentication, rate-limit and outage errors no longer deactivate every model of a Provider either: they are now inconclusive, and change nothing. Reasoning models get enough tokens to pass verification, and Gemini’s embedding, video and Live models are no longer created as chat models.
  • Logs hold less. Secret ciphertext and Account contact details are no longer logged, and the example Secret manifests no longer publish their test passwords.
  • One write per plugin apply. Applying a plugin wrote it three times and sent plugin_updated three times. It now writes once.
  • The built-in metaai LLMClient used a model that Meta no longer offers. It now uses Meta’s current default model.
  • DNS verification checked the API domain’s delegation in the wrong zone, and reported A records that point elsewhere, such as at a CDN, as verified. They are now reported as warnings, and left unchanged.
  • Styles: conversationHeader.color now also colors the chat header’s buttons, which were invisible on six built-in LLMClients, and buttons.color colors only the send and attach buttons.

Upgrading

  • Convert spec.functions to spec.extensions in your LLMClient manifests. The old function links are dropped by the migration, not converted, so an LLMClient that used the weather, calculator or date calculator function has no tools until you list the matching Extension and apply its manifest again. Update system prompts that name the old tools.
  • The registry key. The registry is turned off while SMARTER_EXTENSION_REGISTRY_PUBLIC_KEY is empty. The Helm chart now sets it, and SMARTER_EXTENSION_REGISTRY_URL, to the public registry. If you deploy another way, copy both from .env.example.
  • Set GOOGLE_MAPS_API_KEY. initialize_platform installs and enables the official Extensions, and fills in their secret settings from the platform Secrets of the same name. initialize_providers creates the google_maps_api_key Secret from GOOGLE_MAPS_API_KEY. Without it, the weather Extension can’t be enabled, because its key is a required setting, and the init job fails.
  • The weather Extension needs version 0.2.1 or later, which fixed a dependency requirement that the platform couldn’t meet.
  • manage.py deploy_example_llmclient is removed. deploy_builtin_llmclients already deploys the example LLMClient from its manifest. Remove it from your own scripts.
  • SDKs: update the Python, Node.js and Go SDKs for the functions to extensions change and the new total_tools field.

What’s next

The next post covers v0.30 to v0.32, which translate the web console into Spanish, French and Brazilian Portuguese, and let users upload a profile image.

The full list of changes is in the v0.27, v0.28 and v0.29 changelogs. To try the release, start with the documentation, or browse the code on GitHub.