---
title: "Asset Component Integration Guide"
description: "Comprehensive integration guide for STACKIT Cloud Framework asset discovery components, including data loaders, interactive explorers, and state layers."
scfAsset:
  category: "guide"
  tags: ["Components", "Architecture", "Astro", "Deep-Linking"]
  maintainers:
    - user: "tobias.mueller"
source_url: "https://framework.stackit.cloud/advisory/assetcontainer/stackit/howto-asset-integration/"
source_file: "docs/advisory/assetcontainer/stackit/howto-asset-integration.mdx"
---

The STACKIT Cloud Framework provides a robust, decoupled architecture for discovering, scoring, and rendering technical architecture assets. This guide outlines the pipeline mechanics, data orchestration layers, and interactive state capabilities driving the automated asset discovery components.

---

## Repository Storage Architecture

To ensure flawless automated ingestion, lifecycle indexing, and strict separation of framework governance, assets must be placed within explicit file system directories.

### File System Path Rules
- **Authoritative Base Path**: Every asset document must be stored under the standardized framework-specific structure: `apps/docs/src/content/docs/[framework]/assetcontainer/[partner-slug]/`.
- **Path Isolation**: Moving or altering this path structure breaks the automatic workspace discovery pipelines and excludes the target asset from the global overview maps.

---

## Strict Frontmatter Validation Schema

All asset configuration blocks must adhere to rigid schema validations to enforce search engine optimization (SEO) targets and robust Large Language Model (LLM) text chunk indexing constraints. Non-compliant files will trigger build-time compilation blocks.

### Field Limits & Configurations
- **description**: Must contain a real, concise summary. It is what readers see on the asset card and in search results. **Aim for 120 to 160 characters**: enough to say what the asset offers. The card shows all of it. Hitting that window earns the asset a **ranking bonus** in the explorer ordering. Missing it costs the bonus and nothing else. The pipeline stays quiet between 120 and 180 characters and warns outside that, where a description is too thin to say anything or gets cut off on the card. What does fail the pipeline: a missing description, a stub under 90 characters, a placeholder ("TODO", "description goes here"), or a description that quotes the length rule instead of describing the asset.
- **tags**: A maximum of **10 tags** is allowed per asset. Each individual tag must not exceed **20 characters**. Three or more tags also contribute to the ranking bonus.

### The asset metadata block

Asset metadata lives under the `scfAsset` key (mirroring `scfTrail` for trails). The former name `frameworkAsset` is still accepted, so existing files keep working; new assets should use `scfAsset`. Optionally, add a `maintainers` list to credit who maintains the asset as a point of contact (shown above the git history).

### Crediting Maintainers

`maintainers` names the people or organizations who look after this asset and act as its point of contact. It renders as a **Maintainers** block above the contributor list of the git history. It is a credit, not a permission: it does not restrict who may edit the file. Trails use the identical field under `scfTrail`.

If you have a STACKIT Git account, name yourself by **username** and nothing else. That is the whole entry:

```markdown
scfAsset:
  maintainers:
    - user: "jane.doe"
```

Everything else about you lives in one place: `settings/contributor-members.json`, maintained by Framework Core. It records which contributor organization you belong to, and which of your mirrored profile fields may be shown: your biography as the role line, your website, your address, and your contribution record. That file is the reason an asset cannot decide what is published about you: **only the person a field describes can grant it**, and a field nobody granted is never even fetched.

Ask Framework Core to add or change your entry. It looks like this:

```json
"jane.doe": {
  "org": "acme-corp",
  "show": { "role": true, "website": true, "activity": true }
}
```

Your organization comes from there too, which is why you do not write `contributor` next to a `user`: nobody should be able to inherit an official partner badge by typing a company name beside their own.

### Your Declaration

Two things have to exist before anything beyond your name appears. Your entry in the membership file, and the declaration behind it. The declaration is where you state, once, what you release for publication: your brand, your content, and which of your profile details may be shown.

You file it yourself. Open an issue in the content repository and pick the **Contributor declaration** template, fill it in, and Framework Core or your framework owner records the result in `settings/contributor-members.json`. Nothing you write in the issue is rendered on the site. It is the record behind what the site is allowed to do.

Until that record exists, the daily pipeline publishes nothing about you, your name included, so the credit reads as an anonymous contributor even when the flags in the membership file are already set. The flags describe what you allow. The declaration is the proof that you allowed it. The pipeline waits for both.

The same route works later. Comment on your own declaration issue to change or withdraw any of it, and ask Framework Core or your framework owner to update your entry. A `role: true` in an asset does nothing, so there is no way to switch a field on from a content file. That is the point: only the person a field describes can release it.

The pipeline mirrors the profiles once a day and copies **only the fields that person allowed**: a website, an address or a biography nobody consented to is never fetched and never stored. Your full name is the exception, because it is the name you are credited under. Until your first mirror runs the entry shows your plain username, so a new maintainer is credited immediately rather than showing an empty row. Once mirrored, an **empty Full name counts as a choice**: the credit then reads "Anonymous contributor" instead of exposing your username. If you would rather keep a recognisable byline without your legal name, set the Full name to a pseudonym, a short form or your initials, and the site shows exactly that.

### The Fields in Full

Every field is optional and they combine freely. An entry only has to carry at least one of `user`, `name`, `contributor` or `email`, so it is never rendered anonymously, and one asset accepts up to **6** entries.

| Field | Purpose |
| :--- | :--- |
| `user` | STACKIT Git username. The lookup key for the mirrored profile, and the fallback label until it has been mirrored. |
| `role` | Sets the role text literally. For people without an account: with a `user`, the biography from their own profile appears instead, and only if they allowed it. |
| `website` | Sets a URL literally, same rule as `role`. |
| `email` | Sets an address literally, same rule as `role`. It renders as its own mail action, so it sits alongside a profile link instead of competing with it. |
| `name` | Overrides the mirrored full name, and credits people who have no Git account at all. It makes the entry a person: initials avatar, role line reading *role · organization*. |
| `contributor` | Slug of a `/contributors/<slug>` profile, for people without an account. On its own it credits the organization, using the profile title, its logo initials and its official partner badge. Ignored next to a `user`, whose organization comes from the membership file. |
| `link` | A profile or homepage URL, typically LinkedIn. It becomes the entry's own link and takes precedence over both the mirrored website and the contributor profile. |

External partners have no account on the instance, so they are credited with the explicit fields instead:

```markdown
scfAsset:
  maintainers:
    - name: "John Smith"
      role: "Platform Engineer"
      contributor: "acme-corp"
      email: "john.smith@example.com"
      link: "https://www.linkedin.com/in/john-smith/"
    - contributor: "acme-corp"
      role: "Managed service"
```

<Aside type="note" title="Unknown keys disappear without a word">
The frontmatter schema drops keys it does not know instead of failing the build. If you invent a field, the build stays green and nothing is rendered, so stick to the seven above.
</Aside>

### Allowed Asset Categories (Enums)
The `category` field within the `scfAsset` configuration block only accepts one of these twelve predefined system enumerations. Any other value fails the build:

| Category | Purpose |
| :--- | :--- |
| `generic` | Fallback for assets that fit no specific category. |
| `blueprint` | Reference architectures and solution designs. |
| `guide` | High-level overviews or step-by-step installation instructions. |
| `code-snippet` | Reusable code parts (Terraform, YAML, CLI). |
| `policy` | Governance, security rules, and compliance templates. |
| `video` | Visual product demonstrations or webinars. |
| `workshop` | Materials for discovery, planning, or migration workshops. |
| `case-study` | Success stories and real-world implementation examples. |
| `whitepaper` | Strategic concepts and deep technical research. |
| `service` | Professional or managed offerings (consulting, support, operations). |
| `software` | Ready-to-use applications, platform extensions, or tools. |
| `runbook` | Step-by-step preparation, execution, validation, and rollback instructions. |

---

## Production Frontmatter Schema Blueprint

Use this compliant metadata template when authoring or onboarding new technical documentation assets into an active workspace container:

```markdown
---
title: "Compliant Asset Title"
# The description below is 150 characters: inside the rewarded window. Write your
# own summary of the page - the pipeline rejects placeholders and any description
# that quotes the length rule instead of describing the asset.
description: "Reference architecture for automated landing zone provisioning on STACKIT, covering network segmentation, IAM guardrails, and Terraform module layout."
scfAsset:
  managed: true
  # Required as soon as managed is true: the Marketplace entry this asset can be
  # booked from. It has to be a marketplace.stackit.cloud address, the card renders
  # no pill for anything else. No entry yet? Write
  # https://marketplace.stackit.cloud/en/products as a stand-in. It holds for 7 days
  # from the day this file is added; after that the weekly sweep tags the asset
  # "wip" and it drops out of the default listing until somebody links the entry.
  marketplaceUrl: "https://marketplace.stackit.cloud/en/products/<entry>"
  category: "blueprint"
  # Up to 10 tags, each at most 20 characters. Both limits are hard.
  tags: ["cloud", "automation", "security"]
  # Optional: credit the maintainers (shown as a point of contact).
  maintainers:
    - name: "Jane Doe"
      role: "Cloud Architect"
  # Optional: show a product logo instead of the contributor logo. Looks for
  # this file in the contributor folder (contributors/<owner>/product.png) and
  # for a light-product.png sibling next to it. Supply both, one per theme, the
  # same way the contributor logo works. Without the light variant the card
  # falls back to the contributor logo there and shows two different brands
  # depending on the theme.
  # productLogo: "product.png"
---
```

---

## Reference Links: `<LinkCard>` and `<LinkChip>`

Every reference that stands on its own must say where it leads before anyone clicks it. Two components do that, and the PR pipeline **blocks** a bare markdown link on its own line in the files your PR touches:

- **`<LinkCard>`** is a standalone panel for a reference that carries its own weight: the closing card at the end of an asset pointing to the repository or primary source, or a reference sitting under its own heading.
- **`<LinkChip>`** is a compact inline pill for a link that is a line of its own inside a list — a bullet that is only a link, or a `**Label**:` followed by one.

```mdx
<LinkCard title="Terraform Provider Documentation" href="https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs" />

- **Runbook**: <LinkChip href="/migration/migrate/">Migration execution phase</LinkChip>
```

Rules worth knowing:

- **A closing source card is recommended, not required.** No gate checks that an asset has one. `check-link-cards.mjs` only checks the shape of the links you do write, so an open asset that documents everything in place is complete without any outbound link. The one hard link requirement lives in the frontmatter, not the body: a **managed** asset must carry `marketplaceUrl`, and it has 7 days from the day it is added before the weekly sweep tags it `wip` for still standing on the placeholder. See the blueprint above.
- **No import needed.** Both components are auto-imported in every content page.
- **The kind comes from the URL, never from you.** Card and chip classify their `href` automatically — STACKIT portal, marketplace, documentation, code hosting, or third party — and a third-party destination is visibly marked as leading off the trail. There is no prop to override this, so a label can never lie about the destination.
- **A link inside a sentence stays a plain markdown link.** The surrounding words already say where it leads, and a chip there breaks the line for no gain.
- **Code is exempt.** Fenced blocks and inline code are commands and identifiers, not references, and same-page `#anchors` are navigation, not destinations.
- **On-site targets are resolved.** The gate verifies that the page behind every on-site card and chip actually exists, so a typo in the path fails the pipeline instead of shipping a dead link.

---

## STACKIT Documentation Sections: `<ScfStackitExcerpt>`

Limits, plans, versions and model lists change on the STACKIT side. Do not copy them by hand. Point to the section of the STACKIT documentation instead. The page then shows it in a frame with its source and date. A scheduled run fetches every section four times a day, so the page stays current without anyone editing it.

```mdx
<ScfStackitExcerpt href="https://docs.stackit.cloud/products/storage/file-storage/basics/limits/#general-limits" />
```

- **The address**: the full English address of a page under `https://docs.stackit.cloud/products/`, with the `#anchor` of the heading where the section starts. The section runs to the next heading of the same level. The German page finds its German section by itself.
- **Finding the anchor**: open the page in the STACKIT documentation and pick the heading in "On this page". The address bar then ends in the anchor.
- **No import needed**: the component is auto-imported like the link components.
- **A line of its own**: put it on its own line with an empty line before and after. Never inside a list or a sentence.
- **When the docs change**: the section follows after the next run. If the page moves or the anchor disappears, your page keeps the last checked version with its date. Framework Core gets a message with the new address.
- **On phones and in presentations**: tables turn into cards on phones. Long tables are split over several slides. "Copy for AI" gets the section as text.
- **Good candidates**: limits, plans, performance classes, version and end-of-life tables. Sections that are mostly product description or long step-by-step instructions are better as a `<LinkCard>`.
- **The model list**: `view="facts" profile="model-serving"` shows the shared models of AI Model Serving as one table. It is the only profile today.

### Preview in the dev container

Run `hike`, `summit` or `patrol` as usual. New excerpts in your files are fetched for the preview and show the note "Local preview, not checked yet". A wrong anchor is named in the terminal before the build, together with the anchors the page really has:

```text
📄 STACKIT excerpts: 2 request(s) for new references. What arrived shows "not checked yet" until the merge.
  /products/messaging/mailout/basics/resource-limits/#limits: no heading with this anchor. Anchors on that page: resource-limits, message-limits, sending-quota, object-resource-limits
```

The preview needs access to docs.stackit.cloud. Without it the build goes on and shows a placeholder. The preview of your pull request also shows a placeholder. After the merge the published page shows the section on the same day.

---

## URL-Driven State Parameters

The explorer interface utilizes real-time history synchronization to ensure shared browser addresses act as immutable, reproducible application layouts. The state parameters are actively tracked:

- **Text filtering state**: Sanitized lowercase text sequences are instantly stored via the `search` query parameter.
- **Framework isolation state**: Specific active architectural category filter states are maintained via the `framework` query parameter.
- **Asset drawer tracking**: Actively expanded inline technical documentation drawer components append the `asset` parameter to the address bar.

---

## Production Integration Workflow

To implement the standardized discovery layout on an architectural landing view, utilize the sequential integration workflow.

<Steps>
1. **Import the Execution Loader**: Open the target page layout file and reference the authoritative build-time ingestion container directly.
   
   ```typescript
   import ScfAssetLoader from '@components/scf/scf-asset-loader.astro';
   ```

2. **Mount the Interface Node**: Embed the component tag inside the markup block. Pass an optional layout scoping value to lock the default interface view to a specific category.
   
   ```astro
   <ScfAssetLoader frameworkSlug="migration"/>
   ```

3. **Verify Local Compilations**: Validate the environment to ensure that the browser parameter tracking and history hooks operate flawlessly without generating compilation warnings.
</Steps>

---

## Live Component Playground (local dev server)

Run the docs locally (VS Code task **🥾 Hike — Start Dev Docs**, or the `hike` command in the container terminal, served on `http://localhost:4321`) and open the interactive playgrounds below. Each page renders the component live and walks through every available configuration option, so it doubles as the authoritative reference for the props you can pass:

- **Asset Loader** — every prop (`frameworkSlug`, `showFilter`, `showSearch`, `assetIds`, `filterByTag`, `filterTagMode`) with copy-paste examples for the global, curated, multi-tag (`filterByTag` takes one tag or an array to OR together; `filterTagMode="all"` requires all), and framework-filtered use cases: `http://localhost:4321/demo/components/demo-scf-loader`
- **Asset Explorer** — the interactive grid/list UI that the loader renders, including the inline detail drawer and deep-linking: `http://localhost:4321/demo/components/demo-scf-explorer`
- **Asset Card** — single-card layout plus automatic framework-label and owner parsing from the file path: `http://localhost:4321/demo/components/demo-scf-card`
- **Logo resolution** — folder-based brand marks and the shared `scf-core.png` fallback: `http://localhost:4321/demo/components/demo-scf-logos`

---

## Central Overview

<div class="scf-overview-card-container">
	<a href="/assets" class="scf-custom-link-card">
		<div class="scf-card-text-box">
			<h4 class="scf-card-title">Explore the Cloud Framework Assets Explorer</h4>
			<p class="scf-card-desc">Return to the main directory to view all live, interactive technical architecture components and managed code blocks.</p>
		</div>
		<div class="scf-card-icon-box">
			<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
		</div>
	</a>
</div>

<style>{`
	.scf-overview-card-container {
		margin: 3rem 0;
		width: 100%;
		box-sizing: border-box;
	}

	.scf-custom-link-card {
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		padding: var(--nds-viewport-spacing-component-150, 24px);
		background: var(--nds-mode-color-background-neutral-surface-default-rest, #18232c);
		border: 1px solid var(--nds-mode-color-border-neutral-subtle-rest, #2e3c48);
		border-radius: var(--nds-viewport-border-radius-container, 16px);
		text-decoration: none !important;
		transition: all 0.2s ease-in-out;
		gap: 24px;
	}

	.scf-custom-link-card:hover {
		background: var(--nds-mode-color-background-neutral-surface-default-hover, #202c36);
		border-color: var(--nds-mode-color-foreground-primary-on-neutral, #00c2cc);
		transform: translateY(-2px);
		box-shadow: 0 8px 24px -8px rgba(0, 194, 204, 0.2);
	}

	.scf-card-text-box {
		display: flex;
		flex-direction: column;
		gap: 6px;
		flex: 1;
		min-width: 0;
	}

	.scf-card-title {
		margin: 0 !important;
		font-family: var(--nds-mode-font-family-heading, 'Univia Pro'), sans-serif;
		font-size: var(--nds-viewport-font-size-heading-content-level-3, 18px) !important;
		font-weight: 700;
		color: var(--nds-mode-color-foreground-neutral-high-contrast, #fff) !important;
	}

	.scf-card-desc {
		margin: 0 !important;
		font-family: var(--nds-mode-font-family-body, 'DIN 2014'), sans-serif;
		font-size: var(--nds-viewport-font-size-body-small, 14px);
		color: var(--nds-mode-color-foreground-neutral-subtle, #9da5ac);
		line-height: 1.4;
	}

	.scf-card-icon-box {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 40px;
		height: 40px;
		border-radius: 50%;
		background: var(--nds-mode-color-background-neutral-component-subtle-rest, #25323d);
		color: var(--nds-mode-color-foreground-neutral-default, #e5eaee);
		flex-shrink: 0;
		transition: all 0.2s ease-in-out;
	}

	.scf-custom-link-card:hover .scf-card-icon-box {
		background: var(--nds-mode-color-foreground-primary-on-neutral, #00c2cc);
		color: var(--nds-mode-color-background-neutral-surface-sunken-rest, #101820);
		transform: translateX(4px);
	}

	@media (max-width: 600px) {
		.scf-custom-link-card {
			flex-direction: column;
			align-items: flex-start;
			gap: 16px;
		}
		.scf-card-icon-box {
			align-self: flex-end;
		}
	}
`}</style>
