---
title: "Assets — architecture and data model"
description: "The SQL schema, access-control model, and action reference for developers customizing or extending the Assets template."
---

# Assets — architecture and data model

This page is for anyone customizing the [Assets](/docs/template-assets) template or extending it: the action inventory, data model, access-control rules, and the places to start when you want to change how Assets works.

## Quick start

Create a new workspace with the Assets template:

```bash
npx @agent-native/core@latest create my-assets --standalone --template assets
```

See [Getting Started](/docs/getting-started) for the rest of the local setup (`cd`, `pnpm install`, `pnpm dev`).

## Action reference

Every operation is a TypeScript file in `templates/assets/actions/`, auto-mounted at `POST /_agent-native/actions/:name`.

**Libraries & organization**

- `list-libraries` / `match-library` — find or disambiguate a brand kit
- `create-library` / `update-library` / `archive-library` / `unarchive-library` / `delete-library` — kit lifecycle
- `duplicate-library` — a private, current-user-owned copy with contents remapped; shares, visibility, generation runs, and handoff sessions are not copied
- `list-library-presets` / `create-library-from-preset` — start from a default look instead of a blank kit
- `create-collection` / `update-collection` — optional style/category groupings inside a kit
- `create-folder` / `update-folder` / `delete-folder` — nestable folders

**Assets**

- `list-assets` / `search-assets` — browse or search by title, description, alt text, prompt, model, media type, status, role, folder, or collection
- `get-asset` / `describe-asset` / `update-asset` — read and edit one asset record
- `delete-asset` / `delete-assets` — single or bulk delete
- `import-asset-from-url` — ingest an external HTTPS image as a reference asset with `sourceUrl` provenance
- `set-canonical-logo` — pin the kit's pixel-perfect logo (flips the asset's `role` to `logo_reference` and `status` to `reference`)
- `export-asset` / `export-image` — return a download URL or artifact for another app

**Generation presets**

- `list-generation-presets` — list a kit's saved recipes before generating
- `create-generation-preset` / `update-generation-preset` / `delete-generation-preset` — author format, model, aspect ratio, text/reference policy, `includeLogo`, and `skeletonSpec`
- `analyze-collection-style` — vision-based brand analysis (palette, composition, lighting) when a collection is underspecified
- `extract-palette-from-references` — derive a color palette from reference assets

**Generating & iterating**

- `generate-image` / `generate-image-batch` — synchronous image generation: one asset, or several stable-`slotId` candidates in one call
- `generate-video` — asynchronous video generation; poll `refresh-generation-run`
- `refine-image` / `edit-image` / `restyle-image` — iterate on an existing asset by feedback, a targeted edit, or a subject-preserving restyle
- `generate-asset` — human-in-the-loop generation that returns the inline picker
- `rerun-generation-run` — rerun an older run's prompt and settings against the current library style
- `get-generation-run` / `refresh-generation-run` — poll a run (video only; image calls are synchronous)
- `variant-slots` / `dismiss-variant-slots` — back the shared live generation tray (`asset-variants` app state)

**Sessions & handoff**

- `create-generation-session` / `update-generation-session` / `get-generation-session` / `list-generation-sessions` — designer handoff carrying the brief, preset, active asset, feedback, and run lineage
- `prepare-generation-session-continuation` — preload a fresh chat with a session's handoff context

**Picker & navigation**

- `open-asset-picker` — browse, search, or pick in the embedded picker, or drive its own generation
- `navigate` / `view-screen` — move the UI to a picker, library, generation, or preset surface, or read current navigation and selection

**Audit (org-admin)**

- `is-audit-admin` — cheap check the UI polls to show or hide the Audit Log nav link
- `list-audit-runs` / `get-audit-run` — paginated feed and full run detail, scoped to the caller's org (or their own runs in single-user mode)
- `export-audit-csv` — bulk CSV export of filtered runs to a presigned download URL

**Creative Context** (shared `@agent-native/creative-context` package, mounted into Assets — see [Cross-app Creative Context reuse](/docs/template-assets-integrations#cross-app-creative-context-reuse))

- `search-creative-context` / `get-context-item` / `record-context-feedback` — retrieval and feedback verbs shared across every app that mounts `setupCreativeContext`
- `manage-context-membership` — submit an Assets asset into a governed Context, pinning immutable bytes and metadata
- `clone-creative-context-asset` — Assets-specific: copy approved governed media into an editable library without exposing the private blob handle

## Data model

All data lives in SQL via Drizzle ORM (binary media lives in object storage, or the local file-upload fallback during development). Schema: `templates/assets/server/db/schema.ts`. Libraries carry the standard `ownableColumns` and a matching framework shares table, so they slot into the per-user / per-org sharing model.

Note: the SQL table names keep the legacy `image_*` prefix from when the app was called Images. They cover videos and other media too.

| Table                            | What it holds                                                                                                                                                                            |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `image_libraries`                | A library — the top-level container grouped by brand, campaign, product, or category. Holds `custom_instructions`, `style_brief`, canonical logo and cover asset refs, and archive state |
| `image_library_shares`           | Framework shares table mapping principals (users or orgs) to roles (viewer, editor, admin) per library                                                                                   |
| `image_collections`              | Style/category groupings inside a library — `style_brief`, `prompt_template`, default aspect ratio and image size                                                                        |
| `asset_folders`                  | Nestable folders inside a library (`parent_id` for hierarchy)                                                                                                                            |
| `image_generation_presets`       | Saved generation recipes — media type, prompt template, aspect ratio, model, and text/reference policy                                                                                   |
| `image_generation_sessions`      | An iterative generate-and-choose session with a brief, status, active asset, and feedback summary                                                                                        |
| `image_generation_session_items` | Candidate assets within a session, each with a role and note                                                                                                                             |
| `image_assets`                   | The asset record — media type, role, status, title/description/alt text, prompt, model, dimensions, MIME type, object/thumbnail keys, and lineage                                        |
| `image_generation_runs`          | The generation audit log — prompt, compiled prompt, model, references, status, errors, and the `source` (`chat` / `ui` / `a2a`) that triggered it                                        |

<DataModel
  id="doc-block-assets10"
  title="Assets data model"
  summary={
    "Libraries are the ownable container; collections, folders, and presets organize them. Sessions drive generate-and-choose; assets and runs hold output and the audit log. Table names keep the legacy image_* prefix but cover all media."
  }
  entities={[
    {
      id: "library",
      name: "image_libraries",
      note: "Top-level ownable container",
      fields: [
        {
          name: "id",
          type: "id",
          pk: true,
        },
        {
          name: "custom_instructions",
          type: "text",
          nullable: true,
        },
        {
          name: "style_brief",
          type: "text",
          nullable: true,
        },
        {
          name: "logo_asset_id",
          type: "id",
          fk: "image_assets.id",
          nullable: true,
        },
        {
          name: "archived",
          type: "boolean",
        },
      ],
    },
    {
      id: "library_shares",
      name: "image_library_shares",
      note: "Framework shares table",
      fields: [
        {
          name: "library_id",
          type: "id",
          fk: "image_libraries.id",
        },
        {
          name: "role",
          type: "text",
          note: "viewer / editor / admin",
        },
      ],
    },
    {
      id: "collections",
      name: "image_collections",
      note: "Style/category groupings",
      fields: [
        {
          name: "library_id",
          type: "id",
          fk: "image_libraries.id",
        },
        {
          name: "style_brief",
          type: "text",
          nullable: true,
        },
        {
          name: "prompt_template",
          type: "text",
          nullable: true,
        },
      ],
    },
    {
      id: "folders",
      name: "asset_folders",
      note: "Nestable folders",
      fields: [
        {
          name: "library_id",
          type: "id",
          fk: "image_libraries.id",
        },
        {
          name: "parent_id",
          type: "id",
          fk: "asset_folders.id",
          nullable: true,
        },
      ],
    },
    {
      id: "presets",
      name: "image_generation_presets",
      note: "Saved generation recipes",
      fields: [
        {
          name: "media_type",
          type: "text",
        },
        {
          name: "prompt_template",
          type: "text",
        },
        {
          name: "model",
          type: "text",
        },
      ],
    },
    {
      id: "sessions",
      name: "image_generation_sessions",
      note: "Iterative generate-and-choose",
      fields: [
        {
          name: "id",
          type: "id",
          pk: true,
        },
        {
          name: "status",
          type: "text",
        },
        {
          name: "active_asset_id",
          type: "id",
          fk: "image_assets.id",
          nullable: true,
        },
      ],
    },
    {
      id: "session_items",
      name: "image_generation_session_items",
      note: "Candidate assets in a session",
      fields: [
        {
          name: "session_id",
          type: "id",
          fk: "image_generation_sessions.id",
        },
        {
          name: "asset_id",
          type: "id",
          fk: "image_assets.id",
        },
        {
          name: "role",
          type: "text",
        },
      ],
    },
    {
      id: "assets",
      name: "image_assets",
      note: "The asset record",
      fields: [
        {
          name: "id",
          type: "id",
          pk: true,
        },
        {
          name: "media_type",
          type: "text",
          note: "image / video",
        },
        {
          name: "status",
          type: "text",
        },
        {
          name: "prompt",
          type: "text",
          nullable: true,
        },
        {
          name: "object_key",
          type: "text",
          nullable: true,
        },
      ],
    },
    {
      id: "runs",
      name: "image_generation_runs",
      note: "Generation audit log",
      fields: [
        {
          name: "model",
          type: "text",
        },
        {
          name: "status",
          type: "text",
        },
        {
          name: "source",
          type: "text",
          note: "chat / ui / a2a",
        },
      ],
    },
  ]}
  relations={[
    {
      from: "library",
      to: "collections",
      kind: "1-n",
    },
    {
      from: "library",
      to: "folders",
      kind: "1-n",
    },
    {
      from: "library",
      to: "assets",
      kind: "1-n",
    },
    {
      from: "sessions",
      to: "session_items",
      kind: "1-n",
    },
    {
      from: "library",
      to: "library_shares",
      kind: "1-n",
    },
  ]}
/>

## Access control

`image_libraries` carries `ownableColumns()` plus its own shares table
(`image_library_shares`), so it slots into the standard visibility model
(`private` / `org` / `public`) and the framework `share-resource`,
`unshare-resource`, and `set-resource-visibility` actions with
`resourceType: "asset-library"` (the legacy `image-library` alias remains
registered for existing grants).

`image_assets`, `image_collections`, and `image_generation_runs` do **not**
carry `ownableColumns` themselves — they inherit access from their parent
`library_id`:

- **List queries** filter by `accessFilter(schema.assetLibraries, schema.assetLibraryShares)` to resolve the caller's accessible library ids first, then query children by those ids.
- **Read by id** calls `resolveAccess("asset-library", libraryId)` — wrapped by the `requireLibrary(id)` helper in `actions/_helpers.ts`.
- **Write** calls `assertAccess("asset-library", libraryId, "editor")` for updates and inserts, `"admin"` for deletes.

Every action that touches an ownable resource resolves `libraryId` first, then
asserts against the parent library — never query `image_assets` without also
pinning `library_id` to a value the caller has access to.

`delete-library` cascades through generation sessions, session items, presets,
assets, runs, collections, and folders before removing the library and its
shares. The underlying storage objects are not reaped (a v2 background job);
this is tolerable today because every asset URL still checks access through
its row, not the object key.

The schema is strictly additive — hosted templates share their production
database across every deploy context, so add columns via
`ALTER TABLE ... ADD COLUMN IF NOT EXISTS ...` in a new migration version in
`server/plugins/db.ts`; never rename or drop a column, and never run
`drizzle-kit push` against production. See [Database](/docs/database#migrations)
for the full migration pattern.

## Customizing it

Assets is a complete, cloneable template. Some practical extension ideas:

- "Add a product catalog connector so product reference shots can be selected by SKU."
- "Add a strict approval queue before generated assets are marked usable for marketing."
- "Add a brand review dashboard that filters failed or low-rated generations by model."
- "Create a workspace-wide default asset library and route Slides image generation through it."
- "Add a new provider behind the image generation interface after checking the latest provider docs."

The agent edits routes, components, actions, skills, and SQL-backed models as
needed:

<FileTree
  id="doc-block-assets11"
  title="Where to look when customizing Assets"
  entries={[
    {
      path: "templates/assets/server/db/schema.ts",
      note: "Drizzle schema — image_libraries, image_assets, image_generation_runs, and the rest",
    },
    {
      path: "templates/assets/actions/create-library.ts",
      note: "Library CRUD — the pattern every ownable-resource action follows",
    },
    {
      path: "templates/assets/actions/generate-image.ts",
      note: "The core single-asset generation entry point",
    },
    {
      path: "templates/assets/actions/generate-image-batch.ts",
      note: "Multi-slot generation used by the UI tray and A2A callers",
    },
    {
      path: "templates/assets/actions/_helpers.ts",
      note: "Shared access-check helpers — requireLibrary, requireLibraryAccess",
    },
    {
      path: "templates/assets/actions/list-audit-runs.ts",
      note: "Org-scoped audit feed — the pattern for any org-admin-only action",
    },
    {
      path: "templates/assets/.agents/skills/library-management/SKILL.md",
      note: "Schema, CRUD, sharing, and cascade-delete rules",
    },
    {
      path: "templates/assets/.agents/skills/logo-composite/SKILL.md",
      note: "The generate-then-composite pipeline and preset skeletons",
    },
    {
      path: "templates/assets/.agents/skills/creative-context/SKILL.md",
      note: "The cross-app reuse ladder and context opt-out contract",
    },
    {
      path: "templates/assets/agent-native.app-skill.json",
      note: "Distribution manifest — hosted URL, MCP server name, exported skills, host adapters",
    },
  ]}
/>

See [Templates](/docs/cloneable-saas) for the full clone, customize, deploy flow, and [A2A Protocol](/docs/a2a-protocol) for cross-app generation.

## Distributing the app skill

`agent-native.app-skill.json` at the template root is the distribution
manifest: it declares the app id (`assets`), the hosted app and MCP URLs, the
MCP server name, which `.agents/skills/*` directories export (and under what
name — `asset-generation` exports as `assets`, while `creative-context`,
`image-generation`, and `a2a-assets` stay internal-only), the `/picker` MCP App
surface, and the list of `hostAdapters` (`codex-plugin`, `claude-marketplace`,
`vercel-skills`, `plain-skill`, `claude-skill`, `chatgpt-mcp`, `generic-mcp`)
to generate packages for.

For the install commands, the packed bundle's folder layout, and how an
external host actually calls the picker or generation actions, see
[Installing the Assets skill in a coding agent](/docs/template-assets-integrations#installing-the-assets-skill-in-a-coding-agent)
on the integrations page.

## What's next

- [**Using Assets from other apps and agents**](/docs/template-assets-integrations) — embedding, A2A, Creative Context, and the packaged skill bundle
- [**Sharing & Privacy**](/docs/sharing#building) — `ownableColumns()` and the visibility model this template relies on
- [**Database**](/docs/database) — migrations, dialects, and `getDb`/`schema`
- [**Actions**](/docs/actions#access-control) — the action system powering every entry above
- [**Assets**](/docs/template-assets) — back to the overview
