Props — Composable Presentation Subsystem¶
Props is StageFreight's typed, discoverable, validated, schema-aware presentation subsystem. Users declare presentation items in config; StageFreight resolves them through typed, validated resolvers to produce normalized structured output.
Two Badge Systems¶
StageFreight has two distinct badge mechanisms that serve different purposes:
Local badge generation (kind: badge)¶
StageFreight's owned asset pipeline. Generates SVG badge files locally using the badge package with full control over fonts, colors, and rendering. Good for branded, version-stamped, or build-status artifacts where you own the image.
- Powered by
badgepackage (local SVG renderer) - Output: committed
.svgfiles referenced via raw URLs - CLI:
stagefreight badge generate
External badge composition (kind: props)¶
StageFreight's presentation registry. Typed, validated resolvers that compose URLs from external badge providers (shields.io, codecov, Go Report Card, etc.). Good for ecosystem-standard badges that pull live data from GitHub, Docker Hub, and other services.
- Powered by
propspackage (resolver/composer) - Output: markdown image references to external URLs
- CLI:
stagefreight props list,stagefreight props render
Both compose through narrator — they can coexist in the same README, on the same or separate lines. Use local badges when you control the data and want branded assets. Use props when you want live external data in a standardized format.
Architecture¶
Props uses two-level dispatch:
- Format — how a prop renders.
badgeis the v1 format. Future formats (image,callout,status-chip) plug in without redesigning the subsystem. Format is intentionally separate from narrator'skindfield. - Type — which resolver handles the prop. Each type (e.g.
docker-pulls,codecov,slsa) maps to a resolver that constructs URLs and metadata.
Separation of concerns: - Resolver = what exists (structured data: ImageURL, LinkURL, Alt) - FormatMarkdown = how it looks (single shared renderer for all resolvers) - Narrator placement = where it goes (existing placement system)
Variant¶
v1 implements classic only — standard markdown badge output. The Variant type exists as an implemented seam: unknown variants are rejected at validation time (not silently defaulted). Future render styles (flat, pill, branded-tile) add switch arms without redesigning the render path.
Validation Model¶
Config validation (src/config/validate.go) checks YAML structure only: kind: props must have a type field. Semantic validation (param checking, resolution) happens at narrator run time via ResolveDefinition(). This is intentional deferred validation — structural errors are caught early, semantic errors surface when resolvers run with real params.
Configuration¶
Props items use kind: props in narrator config:
narrator:
- file: README.md
items:
- id: prop.pulls
kind: props
type: docker-pulls
placement:
between: ["<!-- sf:badges:start -->", "<!-- sf:badges:end -->"]
inline: true
params:
image: prplanit/stagefreight
- id: prop.codecov
kind: props
type: codecov
placement:
between: ["<!-- sf:badges:start -->", "<!-- sf:badges:end -->"]
inline: true
params:
repo: prplanit/stagefreight
branch: main
- id: prop.goreport
kind: props
type: go-report-card
params:
module: github.com/PrPlanIT/StageFreight
- id: prop.ci
kind: props
type: github-actions
params:
repo: prplanit/stagefreight
workflow: build.yml
branch: main
- id: prop.slsa
kind: props
type: slsa
params:
level: "3"
Presentation Overrides¶
Override fields live outside params and apply to any type:
- id: prop.pulls-custom
kind: props
type: docker-pulls
params:
image: prplanit/stagefreight
label: "Docker Pulls" # override auto-derived alt text
link: "https://hub.docker.com/r/prplanit/stagefreight" # override auto-derived link
style: flat-square # override default badge style
logo: docker # override/add shields.io logo
| Override | Behavior |
|---|---|
label |
Overrides auto-derived alt text |
link |
Overrides auto-derived link URL |
style |
Appended as ?style= to shields.io URLs; ignored for native/static providers |
logo |
Appended as &logo= to shields.io URLs; ignored for native/static providers |
Unsupported presentation overrides are silently ignored (not errors). They are cross-cutting hints, not semantic params. Hard errors are reserved for bad params values only.
Key Rules¶
paramscontains only provider-semantic inputs (repo,module,image, etc.)styleandlogoare not params — they flow through presentation overrides only- Unknown keys in
paramsproduce a hard error - Missing required params produce a hard error
CLI Commands¶
stagefreight props list¶
List all available prop types, grouped by category:
stagefreight props categories¶
List categories with type counts:
stagefreight props show <type>¶
Show description, parameters, and example config for a type:
stagefreight props render¶
Resolve a prop and print the resulting markdown:
stagefreight props render --type docker-pulls --param image=prplanit/stagefreight
stagefreight props render --type go-report-card --param module=github.com/PrPlanIT/StageFreight
stagefreight props render --type slsa --param level=3
Provider Types¶
Shields (shields)¶
Types backed by img.shields.io URLs. Support style and logo presentation overrides via RenderOptions (not params).
Native (native)¶
Types using the service's own badge URL (e.g., codecov.io, goreportcard.com, GitHub Actions). Presentation overrides for shields.io (style, logo) are ignored.
Static (static)¶
Fixed image URLs with no dynamic provider params (e.g., SLSA level badge, conventional commits).
Normalization Conventions¶
| Param | Convention | Example |
|---|---|---|
repo |
owner/name (no host prefix) |
prplanit/stagefreight |
module |
Full Go module path | github.com/PrPlanIT/StageFreight |
image |
Docker Hub org/name (no registry prefix) |
prplanit/stagefreight |
branch |
Defaults to empty (provider default) | main |
Escaping¶
Template expansion ({param} replacement) inserts values as-is. This is safe for structured values that naturally fit URL paths (owner/repo, github.com/org/name). For arbitrary text in URL path segments or query values, resolvers apply encoding (url.PathEscape, url.QueryEscape) themselves. This keeps the common case simple while handling edge cases correctly.
Resolution Timing¶
Props resolve at build/narrator-run time, not at view time. Resolvers construct URLs and metadata once; the output is static markdown committed to the repo. No network calls needed at resolution time.
Type Catalog (37 Tier 1)¶
- ci (2): github-actions, circleci
- conventions (2): conventional-commits, semantic-release
- docker (4): docker-pulls, docker-stars, docker-image-size, docker-version
- funding (2): github-sponsors, paypal-donate
- github (6): github-issues-open, github-issues-closed, github-prs-closed, github-last-commit, github-commit-activity, go-version
- misc (1): visitor-count
- quality (3): codecov, go-report-card, go-reference
- release (5): github-release, github-tag, github-license, github-contributors, artifact-hub
- security (5): openssf-scorecard, openssf-best-practices, fossa-license, fossa-security, slsa
- social (7): slack, discord, twitter, bluesky, linkedin, website, contact
Total: 37 types across 10 categories.
Use stagefreight props list for the full list with descriptions and provider info.