Skip to main content
apowerb is an open core. The published package is a complete, generic server — not a crippled build waiting for a licence key.

Absent, not disabled

Commercial capabilities — identity-provider login, multi-factor authentication, token accounting and quotas, billing, B2B prospecting, agent evaluation, the administration panel — are not present in the open-source build. There is no feature flag to flip and no dead code in the image. Two of them are recent, and worth naming because the core keeps a hook where the brick used to be the whole answer:
  • Agent evaluation left the core in 0.2.0. The eval routes and the judge logic belong to the evaluation brick; the core no longer serves the ADK eval_sets / eval_results routes.
  • Supervision across accounts stays in the core, but who may cross accounts is answered by a brick. With none registered, each person sees only their own sessions — see Supervision.
The evaluation brick and an older core do not coexist: the brick redefines model metadata the core also declares. Pin the brick against the core release it was built for rather than mixing versions.
That distinction is observable on a running stack:
A missing key and a key set to false do not say the same thing. The first says the capability does not exist in this edition. Client code should treat an absent key as “not in this edition”, never as “disabled”.

The extension mechanism

Bricks are plugged in by naming their Python module in TH2_EXTENSIONS, as a comma-separated list. Each module exposes a register(registry) function — or, historically, init_overlay(registry). Both entry-point names are accepted.
At startup the loader imports each module in order and calls its entry point, letting it register routers, tools and services against the core registry. Registered routers are mounted after the core’s own. TH2_OVERLAY_MODULE is the older single-module form. It is still honoured and is loaded after everything listed in TH2_EXTENSIONS. With neither variable set, no extension loads and the core stays generic and complete in itself. That property is what makes publishing the core alone possible.

Failures are loud

Any error while loading an extension propagates and stops startup. A broken extension must be visible to the healthcheck, never degrade the service silently. An extension that exposes neither register nor init_overlay raises at load time.

Writing an extension

Installing an extension and loading it are two different steps. Adding the package to the environment does nothing on its own — the module must also be named in TH2_EXTENSIONS. A stack that installs the brick but forgets the variable starts healthy, answers 200, and silently lacks the capability.

Interface slots

The web interface has the same arrangement, in JavaScript. The core declares named slots and never imports what fills them; a brick registers a component for a slot at startup. With no brick, a slot renders null — that is the open-source product, not a degraded mode. Three slots exist today: A brick is an object with a name and a register(registry) function, listed in src/extensions/installed.js:
The list is read at build time, so a brick means building your own image of the interface. The catalogue keys AuthScreen.legalPrefix, AuthScreen.privacyPolicy, AuthScreen.legalAnd and AuthScreen.termsOfUse stay in the core for a brick that wants the translated wording rather than its own.
Your terms, not someone else’s. Up to interface 0.1.22 the sign-in screen linked agent-dev.thaink2.fr/legal — the vendor’s own development instance — from every published build. Any self-hosted install therefore pointed its visitors at a third party’s privacy policy and terms. Since 0.1.23 the core renders nothing there. If your deployment needs a legal notice, it is yours to provide, through this slot.
A flag would not have been enough. NEXT_PUBLIC_OAUTH_EXCLUDE_PROVIDERS hides sign-in buttons, and the code for them still ships in the published bundle. An empty slot has nothing to hide — the component was never imported.