Разделы документации
ОбзорБыстрый стартРедакции и возможностиМодели и поляРедактор контентаМедиатекаЛокализацияПубликация и работа командыAPI, SDK и генерация типовРасширения и инструментыРабочие проектыЗадания, вебхуки и наблюдаемостьАудит и управление даннымиСоветники и доверие к плагинамКорпоративный входПространства, квоты и масштабированиеCommerce и PortalПрава и безопасностьРазвёртывание и обновленияЛицензии и установка пакетовТекущие ограниченияПомощь и диагностикаДанные в кабинетеCore CMSDeveloper PlatformProduction UseWorkflowOperationsComplianceAI AssistantsPlugin TrustEnterprise IdentityEnterprise ScaleEnterprise DeploymentCommerce BundlePortal BundleNotty CMS DocumentationAuth & SecurityContent ModelingDeploymentEcosystem & Packaging ConventionsEditions and First-party ModulesExtensibilityGetting StartedMedia ManagementModule Extraction PathDraft & PublishUpgrade GuideWebhooks & IntegrationsCookbook: Blog with Next.jsCookbook: Custom PluginCookbook: Multilingual SiteOperations DocsBackup AutomationDeployment BlueprintsRunbook — Восстановление БД из бэкапаRunbook — Плановый деплойRunbook — Реакция на инцидентRunbook — Откат релизаRunbook — Горизонтальное масштабированиеRunbook — Ротация секретовRunbook — Major upgradeSecrets ManagementNotty CMS — Capability MapNotty Configuration ModelGenerated App ContractComponents and Dynamic ZonesMiddleware SystemPerformance & Scaling ToolkitDisaster Recovery PlaybookDistribution Model
Документация / Технический справочник

Generated App Contract

Техническое руководство из исходного проекта Notty. Примеры, параметры и эксплуатационные ограничения.

Все редакцииОбновлено 2026-09-30

Status: accepted roadmap spec

Purpose: define the canonical Notty-native contract for generated apps, their ownership model, and the resolution order between core, plugins, schema-driven API, and user-land code.

See also: Distribution Model for the end-to-end delivery pipeline (package architecture, publish contract, upgrade path).

Why This Exists

Notty should be delivered as an installable product, not as a copy of the monorepo source tree.

The generated project must be:

  • installable from published packages
  • upgrade-friendly
  • schema-first for common cases
  • extensible with code for advanced cases
  • cleaner and more explicit than a direct Strapi clone

This document defines the target contract for generated Notty apps. Implementation may temporarily lag behind this contract, but the contract itself is the source of truth.

Core Principles

  1. schema-first by default Content modeling, standard CRUD, and most delivery behavior should come from schemas and admin tooling.

  2. code-level escape hatch by design Advanced users must be able to add custom routes, controllers, services, policies, middlewares, hooks, and app-local plugins inside their own project.

  3. no monorepo-source distribution Generated apps consume published Notty packages and prebuilt runtime artifacts. They must not depend on copying platform internals.

  4. clear ownership boundaries User-owned code and platform-managed artifacts must live in different zones.

  5. additive by default, explicit override only Extensions should compose by default. Any override of existing behavior must be declared intentionally and validated at startup.

Canonical Project Structure

my-notty-app/
├── notty.config.ts
├── package.json
├── .env
├── .env.example
├── tsconfig.json
├── config/
├── schemas/
├── src/
│   ├── plugins/
│   │   └── app.ts
│   ├── routes/
│   │   └── index.ts
│   ├── controllers/
│   ├── services/
│   │   └── index.ts
│   ├── policies/
│   │   └── index.ts
│   ├── hooks/
│   │   └── index.ts
│   └── middlewares/
│       └── index.ts
├── public/
├── data/
├── migrations/
├── seeds/
├── .notty/
└── .output/

Directory Contract

Path Ownership Purpose
notty.config.ts user-owned Main project configuration; generated apps register src/plugins/app.ts here
config/ user-owned Additional project config modules
schemas/ user-owned Content type schemas and schema-level customization
src/plugins/app.ts user-owned Canonical app-local server extension entrypoint for generated apps
src/plugins/ user-owned Additional app-local plugins that the project registers explicitly in notty.config.ts
src/routes/ user-owned Route registration modules imported by src/plugins/app.ts
src/controllers/ user-owned Supporting controller/handler modules imported by routes and services; not auto-loaded
src/services/ user-owned Service registration modules imported by src/plugins/app.ts
src/policies/ user-owned Policy registration modules imported by src/plugins/app.ts
src/hooks/ user-owned Hook registration modules imported by src/plugins/app.ts
src/middlewares/ user-owned Middleware registration modules imported by src/plugins/app.ts
public/ user-owned Static public assets
data/ runtime-owned Local runtime data such as SQLite database and uploads in local mode
migrations/ user-owned Database migrations
seeds/ user-owned Seed scenarios
.notty/ platform-managed Generated metadata, cache, internal state, future codegen output
.output/ platform-managed Nitro build output

Ownership Model

User-Owned

These files and directories are expected to be edited by the project owner:

  • notty.config.ts
  • config/**
  • schemas/**
  • src/**
  • public/**
  • migrations/**
  • seeds/**

Runtime-Owned

These paths exist in the generated app but are not treated as source code:

  • data/**

They may be created or mutated during runtime and should usually stay out of version control.

Platform-Managed

These paths must not be manually edited:

  • .notty/**
  • .output/**

Tooling may regenerate them. Their shape can change across versions without being a public authoring contract.

Supported Extension Model

Notty supports three extension styles:

1. Schema-Driven Extension

Use when you need:

  • content models
  • field definitions
  • relations
  • components and dynamic zones
  • default CRUD and delivery behavior

This should cover most product scenarios.

2. App-Local Code Extension

Use when you need:

  • custom endpoints
  • non-standard business logic
  • orchestration between multiple entities
  • custom access checks
  • custom request pipeline behavior

This is the official escape hatch for advanced use cases.

3. Reusable Plugin Extension

Use when functionality should be shared between multiple projects or distributed as a plugin.

Generated App Binding Semantics

Generated apps do not rely on implicit filesystem magic for server extensions.

The canonical binding contract is:

  1. notty.config.ts imports src/plugins/app.ts
  2. notty.config.ts registers that module in plugins as a runtime registration with explicit app-local metadata (source, rootDir, extensionSource: 'app-local')
  3. src/plugins/app.ts is a regular runtime plugin defined with the public plugin API
  4. src/plugins/app.ts imports project modules from src/routes, src/services, src/policies, src/hooks, and src/middlewares
  5. those modules register behavior through the same PluginContext registrars used by reusable plugins

This means:

  • src/routes/index.ts exports route registration code
  • src/services/index.ts exports service registration code
  • src/policies/index.ts exports policy registration code
  • src/hooks/index.ts exports hook registration code
  • src/middlewares/index.ts exports middleware registration code
  • src/controllers/** contains ordinary support modules imported by route/service modules

For generated apps, this explicit app-local plugin entrypoint is the source of truth. Legacy root-level middlewares/ loading remains a compatibility path for older projects, not the canonical generated-app contract.

Resolution Order

The target resolution order is:

  1. core built-ins and system schemas
  2. installed npm plugins
  3. app-local plugins
  4. project schemas
  5. schema-generated CRUD and delivery contracts
  6. user-land hooks, middlewares, policies, services, controllers, and routes

This order does not mean silent shadowing is allowed.

For generated apps, notty.config.ts should keep the generated appPlugin entry after reusable plugins so project-specific registrations apply last inside the supported extension surface.

Override and Collision Rules

These rules are mandatory for the target contract:

  1. additive by default New routes, services, hooks, middlewares, schemas, and plugin contributions should compose without replacing existing behavior.

  2. silent implicit overrides are forbidden A later-loaded route or service must not quietly replace an earlier one just because of file order or registration order.

  3. collisions fail fast If two layers define the same contract surface and no explicit override mechanism is declared, startup must fail with a diagnostic error.

  4. explicit override only Overriding existing behavior must use an explicit contract, for example an override flag, extension API, or manifest declaration.

  5. platform internals are not extension points User-land code must extend the platform only through supported contracts, not by editing hidden generated files or internal build output.

Precedence Model

The platform classifies every contribution by its extension source — a trusted layer that determines override precedence. The canonical signal comes from runtime registration metadata; reserved names such as @notty/app-local are not sufficient on their own without the matching runtime registration source.

Source Precedence Runtime Signal Mutable?
core 0 (highest protection) Platform-owned runtime only; reserved name @notty/core NO — immutable
plugin 1 Default runtime plugin registration YES — with explicit override from equal or later layer
app-local 2 (lowest protection) Project-owned runtime registration marked as app-local; reserved generated entrypoint @notty/app-local YES — with explicit override from equal layer

Override Rules Per Registry

Registry Collision Behavior Override Support
Routes Same method+path within same plugin → error. Route namespace slug collisions across different plugins fail fast at registration/startup. N/A — routes are namespaced and slug-validated
Services Duplicate name → error unless { override: true } Yes — register(name, impl, { override: true }). Core services cannot be overridden.
Policies Duplicate name → error unless { override: true } Yes — register(name, handler, { override: true }). Core policies (isAuthenticated, isAdmin, isSuperAdmin) cannot be overridden.
Hooks Multiple handlers compose additively N/A — hooks never collide
Middleware Namespaced as pluginName:middlewareName, compose with order field N/A — middleware is namespaced
Data Layer Duplicate singularName / uid → error No override — schema contributions are unique

Override Direction

Override flows forward (from earlier-loaded to later-loaded):

  • app-local (2) can override plugin (1) — with { override: true }
  • plugin (1) can override another plugin (1) — with { override: true }
  • plugin (1) cannot override app-local (2) — reverse direction blocked
  • Nobody can override core (0) — immutable

Implementation

Types and utilities are exported from @notty/types:

  • ExtensionSource — 'core' | 'plugin' | 'app-local'
  • EXTENSION_PRECEDENCE — { core: 0, plugin: 1, 'app-local': 2 }
  • classifyPluginSource(pluginName, { declaredSource? }) — resolves precedence classification, preferring trusted runtime metadata when available
  • RegisterServiceOptions — { override?: boolean }
  • RegisterPolicyOptions — { description?: string; override?: boolean }

Enforcement is in ServiceRegistry and PolicyRegistry in @notty/core.

What User-Land Code May Do

User-land code may:

  • add custom routes through src/plugins/app.ts and src/routes/**
  • add custom controllers and services through src/controllers/** and src/services/**
  • add app-specific policies, hooks, and middlewares through src/policies/**, src/hooks/**, and src/middlewares/**
  • add additional app-local plugins via src/plugins/** and explicit notty.config.ts runtime registrations that mark them as app-local
  • extend schema-derived behavior through documented extension APIs

User-land code may not:

  • mutate platform-managed internals inside .notty/
  • depend on .output/ as an authoring surface
  • rely on undocumented ordering side effects
  • patch platform packages directly inside the generated app

Expectations For @notty/create

@notty/create must:

  • generate the canonical structure defined in this document
  • create only supported public authoring surfaces
  • keep platform-managed zones isolated
  • prepare a clean .gitignore for runtime-generated paths
  • scaffold an upgrade-friendly project skeleton
  • avoid leaking monorepo implementation details into the generated app

@notty/create must not:

  • dump platform source code into the project
  • generate undocumented folders as public authoring surfaces
  • blur the line between user-owned and platform-managed files

Upgrade-Friendly Boundaries

To preserve a maintainable upgrade path:

  • published packages own platform behavior
  • generated apps own project behavior
  • generated internals must remain replaceable by tooling
  • user modifications must stay inside documented user-owned zones

If an upgrade requires touching user-owned files, the change must be surfaced through explicit migration guidance, not hidden regeneration.

Non-Goals

This contract intentionally does not try to:

  • reproduce Strapi folder layout one-to-one
  • expose every internal platform layer as public API
  • make admin-generated logic the only customization path
  • allow undocumented override magic based on registration order

Implementation Status

Requirement Status Notes
@notty/create generates canonical structure Done All ownership zones are correctly isolated
Ownership manifest (.notty/ownership.json) Done Machine-readable manifest generated at scaffold time
README includes file ownership section Done Human-readable ownership policy in generated README
@notty/cli upgrade command Done Safe upgrade of @notty/* packages via pnpm dlx @notty/cli upgrade, respects ownership boundaries
notty doctor health check Done Validates project structure post-upgrade
Extensibility APIs Done Plugin, service, policy, hook, middleware registries
Startup validation Partial Override/collision detection is implemented; schema validation at startup is roadmap

Implementation Follow-Up

Remaining items to reflect this spec:

  • docs for middleware, plugins, and project structure
  • migration tooling for breaking changes across major versions

Источник: docs/generated-app-contract.md. Снимок документации исходного проекта. Технический справочник сохраняет язык оригинала.