Editions and First-party Modules
Техническое руководство из исходного проекта Notty. Примеры, параметры и эксплуатационные ограничения.
Notty uses a layered packaging model:
- Core: the base CMS engine and developer extension surface.
- First-party modules: commercial product modules owned and supported by Notty.
- Editions: license bundles that enable a set of first-party modules.
- Add-ons: optional vertical bundles, such as commerce or portal modules.
The implementation is a modular runtime hosted by @notty/server with explicit workspace package
boundaries for module contracts and first-party module manifests:
@notty/module-apiowns the stable manifest and registry API.- Extracted module packages own their manifests and route/scope gates.
@notty/modulesowns the static first-party manifest catalog without installing commercial module packages.@notty/edition-*packages install the physical module packages for each sellable edition.@notty/serverremains the runtime host that resolves licenses, checks installed module packages, enforces gates and serves APIs.
Each manifest is registered through a runtime module registry. The registry validates duplicate module keys, duplicate feature keys, unknown dependencies and load order before capabilities are resolved.
Editions
| Tier | Edition | Intended use | Included modules |
|---|---|---|---|
| 0 | Community | Free self-hosted development, evaluation and non-production use | Core CMS, developer platform |
| 1 | Pro | Commercial production sites and small teams | Community plus production-use controls |
| 2 | Business | Teams that need editorial process and operations | Pro plus workflow, operations, compliance, AI and plugin trust |
| 3 | Enterprise | Corporate deployments | Business plus SSO, SCIM, multi-tenancy, scale and deployment controls |
Community remains useful enough to evaluate and build with Notty. Commercial production use is
modeled as a Pro-level capability so we can avoid relying on support-only monetization. The legacy
developer edition value is accepted as a deprecated alias for community in environment and
project config.
Installation Packages
New projects should install exactly one edition package:
pnpm create notty my-project --edition community
pnpm create notty my-project --edition pro
pnpm create notty my-project --edition business
pnpm create notty my-project --edition enterprise
The generated project depends on @notty/server, @notty/core and the selected
@notty/edition-* package. Edition packages then pull only their allowed first-party modules:
| Edition package | Installs |
|---|---|
@notty/edition-community |
@notty/module-cms, @notty/module-developer |
@notty/edition-pro |
Community plus @notty/module-production |
@notty/edition-business |
Pro plus workflow, operations, compliance, AI and plugin trust modules |
@notty/edition-enterprise |
Business plus enterprise auth, scale and deployment modules |
@notty/server does not directly depend on commercial module packages. If a module is requested by
edition, license or override but the matching package is not installed, the module is reported as
blocked and its features remain disabled. This lets us ship cheaper distributions that do not
contain higher-tier commercial module packages at all.
Production Distribution
Community packages publish publicly. Paid edition packages and commercial module packages publish as restricted packages and are distributed through private registry access, edition-specific portable tarball distributions or private Docker images.
The production release flow builds package-level boundaries first, then creates physical distributions:
./scripts/local-publish.sh
pnpm release:verify:editions
pnpm release:dist -- --edition=all
pnpm release:dist:smoke -- --edition=community --manifest-only
pnpm release:docker -- community
pnpm release:dist -- --edition=<edition> creates dist/editions/notty-<edition>-<version>/ with
only the packages required by that edition. This is the distribution to give to customers who need a
portable self-hosted install without exposing higher-tier commercial packages.
Runtime Configuration
Environment variables have priority over notty.config.ts.
NOTTY_EDITION=business
NOTTY_LICENSE_KEY=notty_license_v1...
NOTTY_LICENSE_ENFORCEMENT=strict
NOTTY_ENABLED_MODULES=commerce.bundle
NOTTY_DISABLED_MODULES=business.ai
NOTTY_LICENSE_PUBLIC_KEY is not a production activation setting. Production strict mode verifies
licenses with the built-in trusted Notty public keys shipped in @notty/server. A custom public key
is accepted only for local development/testing when NOTTY_LICENSE_TRUST_MODE=dev and
NODE_ENV !== production.
Equivalent project config:
import { defineConfig } from '@notty/core/config';
export default defineConfig({
database: { url: process.env.DATABASE_URL ?? 'sqlite://./data/notty.db' },
edition: 'business',
license: {
key: process.env.NOTTY_LICENSE_KEY,
enforcement: 'strict',
enabledModules: ['commerce.bundle'],
disabledModules: ['business.ai'],
},
modules: {
'business.ai': { enabled: false },
'commerce.bundle': { enabled: true },
},
});
Development and test environments default to observe: disabled features are reported with an
observation header while calls remain available. Production always enforces strict, including
Community with installed commercial modules; setting observe cannot unlock paid features.
Disabled commercial features return HTTP 402. Production commercial deployments must explicitly
configure strict and a signed notty_license_v1 license. Unsigned keys and licenses signed by
custom keys are rejected.
Offline License Format
Strict self-hosted installs can use a signed offline license:
notty_license_v1.<base64url-json-claims>.<base64url-signature>
The signed claims include licenseId, edition, optional modules, optional customerName,
customerId, domains, seats, installationId, offlineGraceDays, issuedAt, notBefore and
expiresAt. The server verifies the payload with built-in trusted Notty public keys and checks
edition/module grants, validity dates and installation binding. domains, seats and
offlineGraceDays are currently metadata; runtime enforcement of those limits is not implemented.
Notty team members issue licenses from the private NottyPortal repository. The issuer private key and
license generation scripts are intentionally outside the product repository and must not be bundled
with @notty/server or customer distributions:
cd /secure/NottyPortal
NOTTY_LICENSE_ISSUER_PRIVATE_KEY_FILE=/secure/notty-license-root.key \
pnpm --silent license:issue -- --edition=business --customer="Acme Corp" --expires=2027-05-25T00:00:00.000Z
For local development of the licensing flow only:
NOTTY_LICENSE_TRUST_MODE=dev
NOTTY_LICENSE_PUBLIC_KEY=<local-test-public-key>
Admin Capability Endpoint
Authenticated admin clients can read the active edition, license state, modules and features:
GET /api/admin/platform/capabilities
The response uses the shared EditionCapabilities type from @notty/types. The raw license key is
never returned. Admin clients receive sanitized license status, module activation state, enabled
features, locked features and module load order.
Feature Gates
Server code should use the gate helpers when a route or operation becomes edition-specific:
import { requireFeature, requireModule, checkCapabilities } from '~/lib/editions';
await requireFeature('enterprise.sso', event);
await requireModule('business.workflow', event);
const result = await checkCapabilities({
modules: ['business.operations'],
features: ['operations.webhooks'],
});
Use gates at route or service boundaries. Do not hide security basics behind commercial gates: base RBAC, password auth, safe defaults and critical patch paths belong to Core. Enterprise identity features such as SSO and SCIM can be commercial modules.
Module Manifests
First-party modules are declared as manifests:
export const enterpriseAuthModule = defineNottyModuleManifest({
module: {
key: 'enterprise.auth',
displayName: 'Enterprise Identity',
commercial: true,
firstParty: true,
addOn: false,
requiredModules: ['pro.production'],
packageName: '@notty/module-enterprise-auth',
configKey: 'enterpriseAuth',
description: 'SSO and SCIM automation.',
},
features: [
{ key: 'enterprise.sso', owningModule: 'enterprise.auth', commercial: true, ... },
{ key: 'enterprise.scim', owningModule: 'enterprise.auth', commercial: true, ... },
],
adminRouteGates: {
'admin:sso:providers:list': 'enterprise.sso',
},
});
@notty/server derives the edition capability response from the @notty/modules manifest catalog
and then checks whether the matching physical module package is installed. This keeps the commercial
contract stable while allowing distributions to omit higher-tier commercial packages.
Module activation has four states:
active: module is enabled and all required dependencies are active.locked: module is not available for the active edition/license.disabled: module was explicitly disabled by config or environment.blocked: module was requested, but a required module is not active or the matching package is not installed.
Admin route gates are enforced after admin authentication and scope checks through
requireAdminRouteScope. Non-admin-route APIs use service/scope boundaries, for example SCIM is
gated by enterprise.scim, webhooks are gated by operations.webhooks, and platform tenant access
is gated by enterprise.multi-tenancy.
Module Ownership Catalog
The first-party manifest catalog owns the mapping from product modules to routes, scopes and admin
surfaces. Route implementation can stay in @notty/server, but ownership and licensing policy must
live next to the module manifest.
| Module | Admin routes / APIs | Scope gates | Admin surfaces |
|---|---|---|---|
core.cms |
Content, schemas, components, media, settings | Core content/schema scopes | Content, Media, Schemas, Components |
core.developer |
Admin extensions | Extension runtime | Devtools, plugin navigation |
pro.production |
Backups, preview tokens | admin:backups:*, preview:* |
Backups, Preview |
business.workflow |
Releases, assignments | releases:read, releases:write |
My Work, Releases |
business.operations |
Jobs, scheduled jobs, alerts, delivery observability | webhooks:*, jobs:*, scheduled-jobs:*, alerts |
Jobs, Webhooks, Incidents, Delivery |
business.compliance |
Audit logs, activity, governance, retention, legal holds | audit-logs:read, activity:read |
Activity, Audit Logs, Compliance |
business.ai |
AI advisors and governed AI actions | admin:ai:read, admin:ai:write |
AI Audit, AI Scaffolding |
business.plugin-trust |
Plugin trust policy | Plugin trust service gates | Platform / plugin settings |
enterprise.auth |
SSO providers, SCIM | admin:scim:read, admin:scim:write |
SSO, identity automation |
enterprise.scale |
Workspaces, tenant/platform admin guards | admin:workspaces:read, admin:workspaces:write |
Workspaces |
enterprise.deployment |
HA/deployment controls | Deployment service gates | Platform operations |
commerce.bundle |
Commerce domain add-on | Add-on service gates | Commerce-specific extensions |
portal.bundle |
Portal/member-area add-on | Add-on service gates | Portal-specific extensions |
Package Boundary
First-party modules now have stable module keys, config keys, package names and a physical manifest package boundary:
| Package | Responsibility |
|---|---|
@notty/module-api |
Module manifest type, defineNottyModuleManifest, registry checks |
@notty/module-cms |
Core CMS manifest and base feature ownership |
@notty/module-developer |
Core developer platform manifest and admin-extension gate |
@notty/module-production |
Pro production-use manifest, route/scope gates and extracted preview-token/backup services |
@notty/module-workflow |
Business workflow manifest and gates |
@notty/module-operations |
Business operations manifest and gates |
@notty/module-compliance |
Business compliance manifest and gates |
@notty/module-ai |
Business AI manifest and gates |
@notty/module-plugin-trust |
Business plugin-trust manifest |
@notty/module-enterprise-auth |
Enterprise SSO/SCIM manifest and gates |
@notty/module-enterprise-scale |
Enterprise tenant/workspace/quotas manifest and gates |
@notty/module-enterprise-deployment |
Enterprise deployment manifest |
@notty/module-commerce |
Commerce add-on manifest |
@notty/module-portal |
Portal add-on manifest |
@notty/edition-community |
Community install bundle |
@notty/edition-pro |
Pro install bundle |
@notty/edition-business |
Business install bundle |
@notty/edition-enterprise |
Enterprise install bundle |
@notty/modules |
Static first-party manifest catalog grouped by Core/Pro/Business/etc. |
@notty/server |
Runtime host, license resolver, feature gates, API shell and host adapters |
This is the safe extraction path: first move contracts and manifests out of the server and catalog,
then migrate module implementation code behind those package keys one product domain at a time.
The catalog package no longer imports commercial module packages, so installing @notty/server alone
does not install higher-tier commercial packages. The first runtime extractions use a host-adapter boundary:
@notty/module-production owns preview-token and backup business logic while @notty/server
supplies database, import/export, audit, alert and cron-validation helpers and keeps the HTTP routes
stable. The stable keys are pro.production, enterprise.auth, business.workflow,
business.operations, business.ai, and vertical add-ons such as commerce.bundle or
portal.bundle.
See docs/guide/module-extraction.md for the physical extraction workflow and
docs/guide/commercial-release-checklist.md for the pre-release commercial gate.
Источник: docs/guide/editions-modules.md. Снимок документации исходного проекта. Технический справочник сохраняет язык оригинала.