# Understanding the Instatic Site Shell Configuration Model: Core Architecture and Schema Design

> Explore the Instatic site shell configuration model. Learn about its core architecture and schema design, managed by SiteShellSchema, to define your site's structure and settings.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: architecture
- Published: 2026-07-29

---

**The Instatic site shell is the persisted top-level configuration stored in the `site` table that defines everything about a site except its page contents, visual components, and saved layouts, governed by the `SiteShellSchema` in [`src/core/page-tree/siteDocument.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/page-tree/siteDocument.ts).**

The Instatic site shell configuration model serves as the authoritative backbone of every site managed by the CoreBunch/Instatic repository. It encapsulates site-wide settings, responsive breakpoints, style rules, and runtime metadata while deliberately excluding content-heavy entities like pages and components. This architectural separation enables efficient persistence, rapid validation, and granular retrieval of critical site configuration without the overhead of loading entire page trees or visual component libraries.

## What Is the Instatic Site Shell?

In the Instatic architecture, the *site shell* represents the complete persisted configuration for a specific site. It lives as a single row in the **`site`** database table and functions as the single source of truth for site-level metadata. Crucially, the shell explicitly excludes **page contents**, **visual components**, and **saved layouts**—these entities reside in separate rows (`pages`, `components`, `layouts`) and are merged with the shell only at runtime.

The shell's structure is defined by the `SiteShellSchema` in **[`src/core/page-tree/siteDocument.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/page-tree/siteDocument.ts)**, a TypeBox object that generates the corresponding TypeScript `SiteShell` type:

```ts
export const SiteShellSchema = SiteDocumentSchema
export type SiteShell = Static<typeof SiteShellSchema>

```

## Core Schema Fields of the Site Shell

The `SiteShell` type enforces a strict contract through the following fields, each parsed with specific resilience semantics:

| Field | Type | Description |
|-------|------|-------------|
| **`id`** | `string` | Unique database identifier for the site. |
| **`name`** | `string` | Human-readable site name displayed in the admin UI. |
| **`breakpoints`** | `Breakpoint[]` | Responsive design breakpoints parsed by `parseBreakpoint`. |
| **`conditions`** | `ConditionDef[]` (Optional) | Reusable CSS conditions (`@media`, `@container`, `@supports`); invalid entries are dropped. |
| **`settings`** | `SiteSettings` | Site-wide defaults; missing values fall back to `DEFAULT_SITE_SETTINGS` from [`src/core/site-settings/defaults.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/site-settings/defaults.ts). |
| **`styleRules`** | `Record<string, StyleRule>` | Registry of CSS style rules parsed tolerantly—malformed entries are silently ignored. |
| **`files`** | `SiteFile[]` | Site-level assets and scripts; each parsed by `parseSiteFile` with tolerance for malformed blobs. |
| **`explorer`** | `SiteExplorerOrganization` | Editor-specific metadata for the Site Explorer UI. |
| **`packageJson`** | `SitePackageJson` | Minimal package representation defaulting to `{ dependencies: {}, devDependencies: {} }` if missing. |
| **`runtime`** | `SiteRuntimeConfig` | Runtime scripts and styles normalized by `normalizeSiteRuntimeConfig`. |
| **`createdAt` / `updatedAt`** | `number` | Unix timestamps marking creation and last update; required for persistence. |

**Resilience Semantics:** The tolerant parser `parseSiteDocument` throws immediately on missing *required* fields (`id`, `name`, `breakpoints`, timestamps) but supplies safe defaults for optional sections like settings, packageJson, and runtime configuration.

## Loading, Validation, and Persistence

The site shell follows a rigorous validation pipeline when fetched or stored:

1. **API Retrieval:** The CMS handler `GET /admin/api/cms/site` returns only the shell (excluding pages and visual components). It delegates parsing to **`validateSite`** in [`src/core/persistence/validate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/persistence/validate.ts).

2. **Tolerant Parsing:** `validateSite` invokes `parseSiteDocument` to parse the raw JSON. This function validates required fields while leniently accepting partial optional data.

3. **Post-Check Normalization:** After parsing, `runShellPostChecks` (lines 405-436 in [`src/core/persistence/validate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/persistence/validate.ts)) enforces cross-cutting invariants:
   - **`normalizeSiteFiles`** – filters invalid file entries.
   - **`normalizeSitePackage`** – ensures a valid `packageJson` structure.
   - **`normalizeSiteRuntimeBlock`** – fills missing script and style entries.
   - **`normalizeFrameworkColors`** – guarantees framework color tokens exist.

```ts
export function validateSite(raw: unknown): SiteShell {
  const shell = parseSiteDocument(raw);
  return runShellPostChecks(shell);   // https://github.com/CoreBunch/Instatic/blob/main/src/core/persistence/validate.ts#L405-L436
}

```

4. **Database Conversion:** The repository in **[`server/repositories/site.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/repositories/site.ts)** provides `shellToStorage` and `readStoredShell` to convert between the TypeScript `SiteShell` type and the database row format.

## Architectural Separation: Site Shell vs. Site Document

The full in-memory representation, `SiteDocument`, extends the shell with content entities but never persists them together:

```ts
export type SiteDocument = SiteShell & {
  pages: Page[];
  visualComponents: VisualComponent[];
  layouts: SavedLayout[];
}   // https://github.com/CoreBunch/Instatic/blob/main/src/core/page-tree/siteDocument.ts#L102-L106

```

This separation is enforced architecturally by the test in **[`src/__tests__/architecture/no-vc-in-site-shell.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/__tests__/architecture/no-vc-in-site-shell.test.ts)**, ensuring the shell remains lightweight and content-agnostic.

## Programmatic Examples

### Creating a New Shell Programmatically

When bootstrapping sites in migrations or CLI tools, construct a plain object matching `SiteShellSchema` and pass it through the tolerant parser:

```ts
import { SiteShellSchema, parseSiteDocument } from '@core/page-tree';
import { Type } from '@core/utils/typeboxHelpers';

const rawShell = {
  id: 'site-123',
  name: 'My Blog',
  breakpoints: [{ name: 'mobile', width: 480, icon: 'mobile' }],
  settings: {},               // filled with defaults
  styleRules: {},
  files: [],
  explorer: {},
  packageJson: {},
  runtime: {},
  createdAt: Date.now(),
  updatedAt: Date.now(),
};

const shell = parseSiteDocument(rawShell);
console.log('Validated shell:', shell);

```

### Updating the Shell via CMS API

Client-side admin tools can update configuration through the REST API, using the schema for response validation:

```ts
import { apiRequest } from '@core/http';

async function updateShell(newShell: Partial<SiteShell>) {
  const res = await apiRequest('/admin/api/cms/site', {
    method: 'PUT',
    body: { site: newShell },
    schema: SiteShellSchema,
  });
  console.log('Updated shell version:', res.site.updatedAt);
}

```

### Accessing Shell Data in the Admin UI

React hooks in the admin interface consume the shell as typed data:

```ts
import { useAdminBoot } from '@/admin/preauth/useAdminBoot';
import { SiteShell } from '@core/page-tree';

function useSiteName() {
  const { shell } = useAdminBoot();   // `shell` is a `SiteShell`
  return shell?.name ?? 'Untitled';
}

```

## Summary

- The **Instatic site shell** is the central configuration object stored in the `site` table, deliberately excluding page content, visual components, and layouts to optimize persistence.
- **Schema validation** relies on TypeBox via `SiteShellSchema` in [`src/core/page-tree/siteDocument.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/page-tree/siteDocument.ts), with `parseSiteDocument` providing tolerant parsing that throws only for required field violations.
- The **validation pipeline** in [`src/core/persistence/validate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/persistence/validate.ts) enforces data integrity through `runShellPostChecks`, which normalizes files, packages, runtime configs, and color tokens.
- **Architectural boundaries** prevent content pollution of the shell, enforced by the composite `SiteDocument` type and the `no-vc-in-site-shell` architectural test gate.

## Frequently Asked Questions

### What distinguishes the site shell from the full site document in Instatic?

The site shell contains only configuration metadata such as breakpoints, settings, and runtime flags, while the full site document represents the runtime merger of the shell with `pages`, `visualComponents`, and `layouts`. The shell persists independently in the database, whereas the full document is assembled in memory only when needed.

### What happens if required fields are missing from the site shell during validation?

The tolerant parser `parseSiteDocument` throws a validation error for missing required fields including `id`, `name`, `breakpoints`, and the timestamp fields. However, for optional sections such as `settings`, `packageJson`, or `runtime`, the system applies default values—drawing from `DEFAULT_SITE_SETTINGS` or normalization functions rather than failing.

### How are site-level files and runtime configuration handled in the shell?

The `files` array contains `SiteFile` objects representing scripts, styles, and assets, while the `runtime` field holds `SiteRuntimeConfig` specifying active scripts and style locks. During validation, `normalizeSiteFiles` filters malformed entries and `normalizeSiteRuntimeBlock` ensures missing entries are populated with safe defaults, allowing the shell to tolerate partial data ingestion.

### Where is the site shell stored and how is it converted for database persistence?

The shell persists as a single row in the `site` table. Conversion between the TypeScript `SiteShell` type and the database format is handled by **`shellToStorage`** and **`readStoredShell`** in [`server/repositories/site.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/repositories/site.ts) (lines 31-46), which manage serialization of complex nested objects like `styleRules` and `explorer` metadata.