# Extension architecture Internal Fastify plugins (organise server internals) are a deliberately different concept from **EPPP extensions** (the public product contract). An EPPP extension never receives unrestricted Fastify/Kysely access. ## Manifest v1 (§16.2) `ExtensionManifestV1`: id, name, version, `extensionApiVersion: "1"`, minimumCoreVersion, type (`theme`|`feature`|`content`|`integration`), dependencies, capabilities, settingsSchema, contributes (routes/contentTypes/blocks/sections/themes/jobs/events/admin). IDs must be namespaced (`org.eppp.reading`, `org.eppp.theme-amber`, `com.example.gallery`); unqualified IDs (`reading`, `theme1`) are rejected. ## Extension context v1 (§16.3) `ExtensionContextV1` exposes only narrow EPPP-owned services: `extension`, `settings`, `routes`, `contentTypes`, `blocks`, `sections`, `themes`, `jobs`, `events`, `storage`, `logger`. Raw Fastify instance, Kysely instance, pg pool, and application internals are never exposed through the public SDK. ## Lifecycle states (§16.4) `installed → disabled / enabled / error / incompatible`. Disabled extensions retain data/settings but deactivate contributions. Incompatible or failed optional extensions provide diagnostics without taking unrelated public features down. ## Trust model (§16.5) Extensions are **trusted deploy-time code**: package exists in build config → installed during image build → EPPP discovers/validates → admin enables/configures. No v1 workflow uploads and executes arbitrary npm packages. A future marketplace is a separate security architecture (provenance, signatures, scanning, capability enforcement, isolation, rollback). ## Activation sequence (§17) ``` manifest valid? → API version supported? → core version satisfies range? → dependencies present? → migrations successful? → settings validate? → activate contributions ``` Failures map to `incompatible` / `error` / `config-error` states; diagnostics visible in admin and structured logs. ## Extension package layout (§61) ``` extensions/reading/ package.json · eppp.manifest.json server/{index.ts, migrations/, jobs/, repositories/} admin/index.tsx · public/islands/ · styles/ · tests/ ``` Only documented entry points are importable; extensions never import arbitrary EPPP internal files. ## Engineering conventions (§62) No raw Fastify/Kysely/pg in the SDK; no generic `everythingContext`; no direct extension-to-extension internal import; real needs drive hooks (avoid speculative hook explosion); server/database internals never enter browser bundles; stored content references stable semantic IDs; framework upgrades hidden behind EPPP contracts; architecture rules automated in CI when mechanically enforceable. ## Validation (§22) JSON Schema is canonical for HTTP input, selected HTTP output, extension manifests/settings, block props, page section settings, import/export documents, and selected event payloads. TypeBox authors typed schemas; Ajv validates runtime data. **TypeScript types are not runtime validation.**