ข้ามไปยังเนื้อหาหลัก
BlueForge

Sidecars

Describe your product with two YAML files

A sidecar is two files at the root of your repo. They travel with every deploy and are the source of truth for what the catalog says about your product — name, links, imagery, features and pricing.

What a sidecar is

FileHolds
blueforge.product.ymlIdentity: id, name, tagline, status, links, assets, locales
blueforge.features.ymlMarketing message: feature bullets, categories, tags, pricing, promotions

On a platform deploy, forge-control reads both files, validates them with @blueforge-studio/blueforge-sidecar-schemas and sends them with the deployment registration. From your own CI you send the same content yourself in the product and features fields of POST /api/deployments. The site merges the two into one record per product.

The split is deliberate: identity changes rarely and is reviewed; copy can change on any deploy without touching the identity file.

Minimal example

blueforge.product.yml
# blueforge.product.yml — identity
version: "2.0.0"
products:
  - id: acme-store
    name: Acme Store
    apps: [site]
    primaryApp: site
    status: Live
    tagline: Inventory and orders in one place
    description: >-
      Acme Store keeps stock, orders and fulfilment in sync across every
      channel you sell on.
    ecosystems: [business]
    links:
      site: https://acme.example.com
      others:
        - label: Documentation
          href: https://acme.example.com/docs
    assets:
      logo: public/brand/logo-512.webp
      og: public/brand/og-1200x630.webp
      cover: public/brand/cover.webp
      screenshots:
        - path: public/screens/orders.webp
          caption: Orders from intake to delivery
          alt: The orders dashboard
    i18n:
      locales: [en, es]
blueforge.features.yml
# blueforge.features.yml — marketing message
version: "2.0.0"
features:
  - product: acme-store
    features:
      - Stock levels synced across channels
      - One order queue for every storefront
    categories:
      - slug: e-commerce
        label: E-commerce
    tags: [inventory, orders]
    pricing:
      model: freemium
      currency: USD
      tiers:
        - name: Free
          price: "$0"
        - name: Pro
          price: "$19/mo"
          recommended: true
promotions:
  - code: LAUNCH20
    label: 20% off your first three months
    valid_until: "2026-12-31"

Product fields

The file is { version: "2.0.0", products: [...] } with at least one entry. Every object is strict: an unknown or misspelled key is a validation error, not a silently ignored field.

FieldRules
idRequired. The product's catalog key — see Identity below.
nameRequired. Display name.
apps / primaryAppAt least one is required. The app names (as in blueforge.config.json) that belong to this product; primaryApp is the one whose URL the catalog links.
statusOne of Live, Beta, In Development, Coming Soon, Planned.
tagline, description, longDescription, summaryCopy. summary is one short sentence used on cards.
ecosystemsList of ecosystem ids the product appears under.
icon, color, gradientCard styling hints (icon name and Tailwind classes).
linkssite, app, and others — a list of { label, href } where href must be a URL.
assetslogo, og, cover (repo-relative paths) and screenshots — a list of { path, caption?, alt? }.
i18n.localesThe languages the product ships (BCP-47 tags, at least one).
collectionOptional kebab-case id grouping related products into one family.

Features fields

The file is { version: "2.0.0", features: [...], promotions?: [...] }. Each features[] row names the product it describes with product:, which must match an id in the product file.

FieldRules
productRequired. The product id this row describes.
featuresFeature bullets. A plain string, or an object with text plus an image and its alt text.
categoriesList of { slug, label? }; slug is kebab-case.
tagsList of strings.
pricingmodel (free, freemium, subscription, one-time, contact, enterprise), optional ISO-4217 currency, and at least one tier { name, price, description?, recommended?, includes?, cta? }. At most one tier may be recommended. pricing: null hides pricing deliberately.
promotionsTop level, up to 20: { code, label, url?, valid_from?, valid_until?, terms? }; dates are YYYY-MM-DD and valid_from may not be after valid_until.

Identity and precedence

  • The id is the load-bearing key. Changing it creates a second catalog card; the old id stays until its registration is retired. Pick it once.
  • The sidecar wins. Once a deploy has registered a sidecar for a product, its copy overrides any other description of the same id the catalog holds.
  • First writer owns the app. A registration is keyed by (repo, app) and belongs to the organization that registered it first; another organization writing the same pair is refused with 403 tenant-mismatch.

Assets

Asset paths are repo-relative. The platform hashes each declared file and uploads only the ones whose sha256 changed; the site renders every size it needs from the upload. Any format the image pipeline decodes is accepted (png, jpeg, webp, gif, avif, svg, tiff).

KindUsed for
logoCards, product page, search — square, transparent preferred
ogog:image / twitter:image on the product page (1200×630)
coverProduct page hero and listing cards; the social card when there is no og
screenshotsThe product page gallery, in the order declared

The upload protocol, the fallbacks and the approval gate are on Asset sync.

Versioning

Both files carry version: "2.0.0", the current schema version. The registration endpoint also accepts the older single-file shape (one product's fields at the top level of blueforge.product.yml), so an older repo keeps registering while it migrates — but new repos should write the two-file form above.

Imagery is versioned by content: a replaced file is a new object at a new URL, so caches never serve a stale image under a fresh name.

Schema changes

The schemas are strict, so a new field is only accepted once your pinned version of the schemas package knows it. Bump the package in the same change that starts using a new field.

Validate in CI

Parse the YAML and run it through the schema before you deploy; a failure lists every issue with its path.

validate-sidecars.ts
import { readFileSync } from "node:fs";
import { parse } from "yaml";
import { parseProductSidecar, parseFeaturesSidecar } from "@blueforge-studio/blueforge-sidecar-schemas";

const product = parseProductSidecar(parse(readFileSync("blueforge.product.yml", "utf8")));
const features = parseFeaturesSidecar(parse(readFileSync("blueforge.features.yml", "utf8")));

for (const r of [product, features]) {
  if (!r.ok) {
    console.error(r.issues);
    process.exit(1);
  }
}
Install the schemas package from packr — see Packages. Any YAML parser works; yaml is used here as an example.

ติดตามข่าวสาร

บันทึกการอัปเดตและสิทธิ์เข้าใช้ก่อนใคร

ส่งอีเมลหนึ่งฉบับเมื่อมีอะไรใหม่ปล่อยออกมา: ฟีเจอร์ใหม่ของแพลตฟอร์ม แอปใหม่ และคำเชิญร่วมงานเปิดตัว เข้าร่วมรายชื่อรอเพื่อรับสิทธิ์ใช้งานก่อนใครสำหรับการสมัครด้วยตนเองและเว็บไซต์โปรโมชัน

ไม่มีสแปม ยกเลิกการรับได้ด้วยคลิกเดียว