Manifest Model
This page is the exhaustive reference for Shoehorn’s native manifest schema, schemaVersion: 1. Use it when you need the full object model for .shoehorn/manifest.yml, not just a quick-start example.
File Location
Section titled “File Location”Shoehorn looks for manifests in these common locations:
.shoehorn/manifest.yml.shoehorn/**/*.yml.shoehorn/**/*.yamlcatalog-info.yamlfor Backstage compatibility
Top-Level Fields
Section titled “Top-Level Fields”| Field | Required | Type | Notes |
|---|---|---|---|
schemaVersion | Yes | integer | Must be 1 |
annotations | No | object | String key-value metadata |
integrations | No | object | Changelog, runbooks, licenses |
description | No | string | Top-level description |
service | Yes | object | Core entity identity |
owner | Yes* | array | At least one owner, except catalog-source |
lifecycle | No | string | Lifecycle stage |
tags | No | array | Lowercase kebab-case tags |
links | No | array | External links |
interfaces | No | object | HTTP and gRPC metadata |
relations | No | array | Entity relationships |
extras | No | array | Slack channels, discovery pointers, custom extras |
docs | No | object | Documentation paths |
Complete Example
Section titled “Complete Example”schemaVersion: 1
annotations: github.com/project-slug: acme/payment-api pagerduty.com/service-id: P123456
integrations: changelog: path: CHANGELOG.md runbooks: - title: Payment API incident response path: docs/runbooks/payment-api.md severity: critical licenses: - title: Stripe API vendor: Stripe purchased: "2026-01-15" expires: "2027-01-14" seats: 1 cost: "$2000/year" contract: STRIPE-2026 notes: Auto-renews unless canceled
description: Handles payment authorization, capture, and refunds.
service: id: payment-api name: Payment API type: service tier: high description: Public-facing payments backend members: - payments-oncall@example.com - payments-techlead@example.com
owner: - type: team id: payments-team - type: manager id: jane@example.com
lifecycle: production
tags: - payments - critical - pci
links: - name: Repository url: https://github.com/acme/payment-api icon: GitHub - name: Grafana url: https://grafana.example.com/d/payment-api icon: Grafana
interfaces: http: baseUrl: https://api.example.com/payments openapi: docs/openapi.yaml version: v1 auth: type: oauth2 rateLimits: requestsPerMinute: 600 requestsPerHour: 10000 graphql: endpoint: /graphql schema: schema/payment-api.graphql grpc: package: acme.payments.v1 proto: proto/payment.proto services: - PaymentService - RefundService protos: - proto/payment.proto - proto/refund.proto
relations: - type: depends_on target: database:payments-db notes: Primary OLTP database - type: publishes_to target: topic:payment-events - type: part_of target: system:payments-platform
extras: - type: slackchannel target: "#payments" - type: catalog-discovery-file target: .shoehorn/manifest.yml
docs: readme: path: README.mdservice
Section titled “service”The service block defines the entity’s identity and type.
| Field | Required | Type | Notes |
|---|---|---|---|
id | Yes | string | Lowercase kebab-case, unique within the tenant |
name | Yes | string | Human-readable display name |
type | Yes | string | Entity type |
tier | No | string | Declared service criticality |
description | No | string | Alternative to top-level description |
members | No | array | Email addresses, mainly for team entities |
Supported service.type values:
apiservicefrontenddatabaselibraryteamextrassystemresourcecatalog-sourcedomain
Supported service.tier values:
criticalhighmediumlow
The metal words platinum, gold, silver, and bronze still work as legacy aliases. They map to critical, high, medium, and low.
owner is an array of owner references. Each item has the same shape:
| Field | Required | Type | Notes |
|---|---|---|---|
type | Yes | string | Owner role or owner kind |
id | Yes | string | Team slug, email address, or other identifier |
Common owner types:
teamuserproductOwnerdesignerstakeholdersupportmanager
owner is required for every entity except catalog-source.
annotations
Section titled “annotations”annotations is a free-form string map for metadata that does not belong in the structured model.
Example:
annotations: github.com/project-slug: acme/payment-api backstage.io/techdocs-ref: dir:.integrations
Section titled “integrations”integrations groups optional metadata about changelogs, runbooks, and commercial licenses.
integrations.changelog
Section titled “integrations.changelog”| Field | Required | Type | Notes |
|---|---|---|---|
path | No | string | Path relative to repository root |
integrations.runbooks[]
Section titled “integrations.runbooks[]”| Field | Required | Type | Notes |
|---|---|---|---|
title | Yes | string | Display name |
path | No | string | Path relative to repository root |
severity | No | string | critical, high, medium, low |
integrations.licenses[]
Section titled “integrations.licenses[]”| Field | Required | Type | Notes |
|---|---|---|---|
title | Yes | string | License or contract name |
vendor | No | string | Vendor name |
purchased | No | string | ISO date string |
expires | No | string | ISO date string |
seats | No | integer | Minimum value 1 |
cost | No | string | Human-readable cost |
contract | No | string | Contract identifier |
notes | No | string | Free-form notes |
lifecycle
Section titled “lifecycle”Supported lifecycle values:
productionstagingdevelopmentdeprecatedexperimental
tags must be lowercase kebab-case strings.
Example:
tags: - payments - internal-apilinks[]
Section titled “links[]”Each link object has this shape:
| Field | Required | Type | Notes |
|---|---|---|---|
name | Yes | string | Display label |
url | Yes | string | Must start with http:// or https:// |
icon | No | string | Optional icon name |
interfaces
Section titled “interfaces”interfaces exposes API and protocol metadata.
interfaces.http
Section titled “interfaces.http”| Field | Required | Type | Notes |
|---|---|---|---|
baseUrl | No | string | Base URL for the HTTP API |
openapi | No | string | File path or URL |
version | No | string | Version label, for example v1 |
auth.type | No | string | oauth2, apikey, none |
rateLimits.requestsPerMinute | No | integer | Requests per minute |
rateLimits.requestsPerHour | No | integer | Requests per hour |
rateLimits.requestsPerDay | No | integer | Requests per day |
graphql.endpoint | No | string | Path or URL |
graphql.schema | No | string | Path or URL |
interfaces.grpc
Section titled “interfaces.grpc”| Field | Required | Type | Notes |
|---|---|---|---|
package | No | string | gRPC package name |
proto | No | string | Single proto path or URL |
services | No | array | Service names |
protos | No | array | Multiple proto paths or URLs |
relations[]
Section titled “relations[]”Each relation object has this shape:
| Field | Required | Type | Notes |
|---|---|---|---|
type | Yes | string | Relation type |
target | Yes | string | <type>:<identifier> |
notes | No | string | Extra context |
Supported relation types:
depends_onpublishes_toconsumes_apiprovides_apideployed_tocallsreads_fromwrites_tosubscribes_tousesviapart_of
Supported relation target prefixes:
service:database:topic:team:channel:link:oncall:system:resource:
extras[]
Section titled “extras[]”extras is for metadata that does not fit the main model but still belongs on the entity.
| Field | Required | Type | Notes |
|---|---|---|---|
type | Yes | string | Extra type |
target | Yes | string | Value for that extra |
Supported extra types:
slackchannelteamLinkscustomcatalog-discovery-urlcatalog-discovery-file
The docs block currently supports README discovery:
| Field | Required | Type | Notes |
|---|---|---|---|
readme.path | Yes | string | Path relative to repository root |
descriptioncan live at the top level or insideservice.description.docs.readme.path,integrations.changelog.path, andintegrations.runbooks[].pathare relative to the repository root.- Backstage
catalog-info.yamlfiles can be imported, but Shoehorn’s native manifest model is the schema on this page.
See Also
Section titled “See Also”- Manifests - Quick-start manifest guide
- Manifest Validation - Schema rules and common validation failures
- Repository Ownership - How GitHub ownership signals interact with manifests