Documentation
¶
Overview ¶
Package catalog — build.go: BuildDocument and all entity/filter helpers.
Package catalog — marshal.go: MarshalDocument and format serialization.
Package catalog — redact.go: privacy redaction for status board entries.
Package catalog provides the Backstage-style wire format and BuildDocument function for GoCell's devtools catalog endpoint.
Wire types live here (not in kernel/metadata) so that downstream products can import kernel/metadata for the project model without being locked into this particular wire format.
ref: backstage/backstage packages/catalog-model/src/entity/Entity.ts@master ref: backstage/backstage docs/features/software-catalog/well-known-relations.md@master
Index ¶
- Constants
- Variables
- func InjectWireSummaries(entities []Entity, summaries []metadata.CellWireSummary)
- func MarshalDocument(d Document, format string) ([]byte, error)
- type ActorSpec
- type AssemblySpec
- type AssemblySpecBuild
- type CellDepGraph
- type CellEdge
- type CellSpec
- type CellSpecL0Dep
- type CellSpecOwner
- type CellSpecSchema
- type CellSpecWire
- type ContractSpec
- type Dependencies
- type Document
- type Entity
- type EntityMetadata
- type ExportOptions
- type Filter
- type FilterEcho
- type IncludeOptions
- type JourneyPassCrit
- type JourneySpec
- type PackageDepsView
- type Relation
- type SliceSpec
- type SliceSpecContractUsage
- type StatusBoardEntry
Constants ¶
const ( SchemaVersionV1 = "v1" APIVersionV1 = "gocell.io/v1alpha1" )
Wire-format constants. Bumping either of these is a breaking change that requires a coordinated update of every consumer (gocell-web, ops dashboards, CI scripts).
Variables ¶
var AllIncludeTokens = []string{"cellDeps", "packageDeps", "relations", "statusBoard"}
AllIncludeTokens lists every accepted ?include= / --include= value. Maintained as the single source of truth so CLI and HTTP cannot drift.
var AllKinds = []string{"Actor", "Assembly", "Cell", "Contract", "Journey", "Slice"}
AllKinds enumerates the entity kinds BuildDocument can emit.
var AllLayers = []string{
"adapters", "actors", "assemblies", "cells", "cmd", "contracts",
"examples", "generated", "journeys", "kernel", "pkg", "root",
"runtime", "stdlib", "tests", "thirdparty", "tools", "unknown",
}
AllLayers enumerates the layers used by entityLayer + tools/depgraph nodes.
Functions ¶
func InjectWireSummaries ¶
func InjectWireSummaries(entities []Entity, summaries []metadata.CellWireSummary)
InjectWireSummaries replaces the Spec of each Cell entity in-place with a CellSpecWire that embeds the original CellSpec plus the matching CellWireSummary (keyed by CellID == Entity.Metadata.Name). Entities whose cell ID has no matching summary get a CellSpecWire with WireSummary == nil (omitted from JSON/YAML output — forward-compatible).
Non-Cell entities are left unchanged. Exported so CLI export and HTTP catalog share the same augmentation path.
func MarshalDocument ¶
MarshalDocument serializes d as JSON or YAML according to format. Returns an error for unrecognized format strings.
- format == "json" → encoding/json with two-space indent
- format == "yaml" → gopkg.in/yaml.v3 default indent
Both formats are byte-deterministic for identical input thanks to:
- Document field declaration order (struct fields encoded in source order)
- kerneldepgraph.Graph.MarshalJSON sorting Packages + Imports
- BuildDocument sorting Entities and Relations before returning
Types ¶
type ActorSpec ¶
type ActorSpec struct {
MaxConsistencyLevel string `json:"maxConsistencyLevel,omitempty" yaml:"maxConsistencyLevel,omitempty"`
}
ActorSpec is Document.Entities[Kind=="Actor"].Spec.
type AssemblySpec ¶
type AssemblySpec struct {
Cells []string `json:"cells,omitempty" yaml:"cells,omitempty"`
Owner CellSpecOwner `json:"owner" yaml:"owner"`
MaxConsistencyLevel string `json:"maxConsistencyLevel,omitempty" yaml:"maxConsistencyLevel,omitempty"`
Build AssemblySpecBuild `json:"build" yaml:"build"`
}
AssemblySpec is Document.Entities[Kind=="Assembly"].Spec.
type AssemblySpecBuild ¶
type AssemblySpecBuild struct {
Entrypoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty"`
Binary string `json:"binary,omitempty" yaml:"binary,omitempty"`
DeployTemplate string `json:"deployTemplate,omitempty" yaml:"deployTemplate,omitempty"`
}
AssemblySpecBuild mirrors AssemblyBuildMeta on the wire.
type CellDepGraph ¶
type CellDepGraph struct {
Nodes []string `json:"nodes" yaml:"nodes"`
Edges []CellEdge `json:"edges" yaml:"edges"`
BuiltAt string `json:"builtAt,omitempty" yaml:"builtAt,omitempty"`
}
CellDepGraph is the cell-level (cell.yaml dependencies field) view. Built by the caller from kernel/governance.Validator.Graph(). Nodes/Edges sorted deterministically.
BuiltAt records when this graph was last constructed (RFC3339 UTC). HTTP clients can use this to detect stale graphs — the field does not change between requests because the graph is built once at bootstrap time.
func NewCellDepGraph ¶
func NewCellDepGraph(g governance.Graph, clk clock.Clock) *CellDepGraph
NewCellDepGraph constructs a CellDepGraph from a governance.Graph snapshot. Both HTTP (bootstrap.phase5InitDevtoolsHandler) and CLI (cmd/gocell export catalog) call this so the wire shape and field-population semantics stay in lock-step. BuiltAt is set from clk so consumers can detect stale graphs.
type CellSpec ¶
type CellSpec struct {
Type string `json:"type" yaml:"type"`
ConsistencyLevel string `json:"consistencyLevel" yaml:"consistencyLevel"`
// Lifecycle is the cell's governance maturity lifecycle (cellvocab.CellLifecycle),
// projected from CellMeta.Lifecycle. Same wire key as ContractSpec.Lifecycle.
// Omitted when cell.yaml does not declare it (consumers treat absence as the
// experimental default, matching NewBaseCell).
// Note: distinct value set from ContractSpec.Lifecycle (maturity
// experimental|candidate|asset|maintenance|retired, not
// draft|active|deprecated).
Lifecycle string `json:"lifecycle,omitempty" yaml:"lifecycle,omitempty"`
DurabilityMode string `json:"durabilityMode,omitempty" yaml:"durabilityMode,omitempty"`
Owner CellSpecOwner `json:"owner" yaml:"owner"`
Schema CellSpecSchema `json:"schema" yaml:"schema"`
VerifySmoke []string `json:"verifySmoke,omitempty" yaml:"verifySmoke,omitempty"`
L0Dependencies []CellSpecL0Dep `json:"l0Dependencies,omitempty" yaml:"l0Dependencies,omitempty"`
// Requires is the assembly capabilities this cell consumes (postgres/redis/rabbitmq).
Requires []string `json:"requires,omitempty" yaml:"requires,omitempty"`
Slices []string `json:"slices,omitempty" yaml:"slices,omitempty"` // canonical "cell.slice" IDs
}
CellSpec is Document.Entities[Kind=="Cell"].Spec.
type CellSpecL0Dep ¶
type CellSpecL0Dep struct {
Cell string `json:"cell" yaml:"cell"`
Reason string `json:"reason,omitempty" yaml:"reason,omitempty"`
}
CellSpecL0Dep mirrors L0DepMeta on the wire.
type CellSpecOwner ¶
type CellSpecOwner struct {
Team string `json:"team" yaml:"team"`
Role string `json:"role" yaml:"role"`
}
CellSpecOwner mirrors OwnerMeta on the wire (camelCase tags).
type CellSpecSchema ¶
type CellSpecSchema struct {
Primary string `json:"primary" yaml:"primary"`
}
CellSpecSchema mirrors SchemaMeta on the wire.
type CellSpecWire ¶
type CellSpecWire struct {
CellSpec
// WireSummary carries the aggregated wire surface for a Cell derived from
// K#05 marker comments (listeners, route mounts, event subscriptions).
//
// Stability: experimental — field shape may change between minor versions.
// Consumers (dashboards / CI) should treat this as a non-stable contract
// until promoted via ADR.
WireSummary *metadata.CellWireSummary `json:"wireSummary,omitempty" yaml:"wireSummary,omitempty"`
}
CellSpecWire extends CellSpec with an optional wire surface summary injected when WireSummaries are present in ExportOptions. The embedded CellSpec ensures JSON/YAML serialization is identical to the base type; WireSummary is an additive optional field (v1 response only-adds policy).
type ContractSpec ¶
type ContractSpec struct {
Kind string `json:"kind" yaml:"kind"`
OwnerCell string `json:"ownerCell,omitempty" yaml:"ownerCell,omitempty"`
ConsistencyLevel string `json:"consistencyLevel,omitempty" yaml:"consistencyLevel,omitempty"`
Lifecycle string `json:"lifecycle,omitempty" yaml:"lifecycle,omitempty"`
Triggers []string `json:"triggers,omitempty" yaml:"triggers,omitempty"`
Replayable bool `json:"replayable,omitempty" yaml:"replayable,omitempty"`
IdempotencyKey string `json:"idempotencyKey,omitempty" yaml:"idempotencyKey,omitempty"`
DeliverySemantics string `json:"deliverySemantics,omitempty" yaml:"deliverySemantics,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
DeprecatedAt string `json:"deprecatedAt,omitempty" yaml:"deprecatedAt,omitempty"`
}
ContractSpec is Document.Entities[Kind=="Contract"].Spec.
Note: this name shadows contractspec.ContractSpec in some import contexts; callers in cmd/ and runtime/ should import this package as `catalog` so the disambiguation reads naturally (`catalog.ContractSpec` vs `contractspec.ContractSpec`).
type Dependencies ¶
type Dependencies struct {
Cells *CellDepGraph `json:"cells,omitempty" yaml:"cells,omitempty"`
Packages *PackageDepsView `json:"packages,omitempty" yaml:"packages,omitempty"`
}
Dependencies aggregates the optional dep-graph blocks. Either field may be nil; serialization elides nil branches via omitempty.
type Document ¶
type Document struct {
SchemaVersion string `json:"schemaVersion" yaml:"schemaVersion"`
APIVersion string `json:"apiVersion" yaml:"apiVersion"`
GeneratedAt string `json:"generatedAt" yaml:"generatedAt"`
Root string `json:"root" yaml:"root"`
Query FilterEcho `json:"query" yaml:"query"`
Entities []Entity `json:"entities,omitempty" yaml:"entities,omitempty"`
StatusBoard []StatusBoardEntry `json:"statusBoard,omitempty" yaml:"statusBoard,omitempty"`
Dependencies *Dependencies `json:"dependencies,omitempty" yaml:"dependencies,omitempty"`
}
Document is the top-level wire envelope. Backstage-style five-field root + schemaVersion / generatedAt / root / query echo. Marshaled deterministically (Entities sorted by (kind, name); each Entity's Relations sorted by (type, targetRef)) so two builds of the same project byte-equal.
func BuildDocument ¶
func BuildDocument(clk clock.Clock, pm *metadata.ProjectMeta, opts ExportOptions) (Document, error)
BuildDocument projects pm into a Document according to opts. Pure function: no I/O, no global state access. Time is sourced from clk.
clk is required and positional; pass clockmock.New(...) in tests. All filter combinations produce a valid (possibly empty) Document; an empty result is not an error.
type Entity ¶
type Entity struct {
APIVersion string `json:"apiVersion" yaml:"apiVersion"`
Kind string `json:"kind" yaml:"kind"` // Cell|Slice|Contract|Journey|Assembly|Actor
Metadata EntityMetadata `json:"metadata" yaml:"metadata"`
Spec any `json:"spec" yaml:"spec"`
Relations []Relation `json:"relations,omitempty" yaml:"relations,omitempty"`
}
Entity is the per-resource wrapper — one per Cell, Slice, Contract, Journey, Assembly, or Actor. Spec carries a kind-specific typed struct (CellSpec, SliceSpec, ...). Encoded as JSON object regardless of the underlying type.
type EntityMetadata ¶
type EntityMetadata struct {
Name string `json:"name" yaml:"name"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
UID string `json:"uid" yaml:"uid"`
File string `json:"file,omitempty" yaml:"file,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
}
EntityMetadata holds Backstage-style metadata fields. Name is the technical identifier; UID is the catalog-stable canonical reference (kind/name).
type ExportOptions ¶
type ExportOptions struct {
// Root is the project root path echoed back in Document.Root. Optional.
Root string
// Filter projects entities and dependencies. Zero value = full snapshot.
Filter Filter
// CellDeps, when non-nil, populates Document.Dependencies.Cells. Caller
// constructs by invoking governance.Validator.Graph() and translating to
// *CellDepGraph.
CellDeps *CellDepGraph
// Packages, when non-nil, populates Document.Dependencies.Packages. The
// pointer carries its own error field so HTTP lazy loading and CLI
// synchronous loading share one shape. Callers signal readiness via
// Graph != nil and errors via Error != "".
Packages *PackageDepsView
// WireSummaries, when non-nil, augments each Cell entity's Spec with a
// wireSummary block derived from cell.go marker comments (K#05). Nil means
// the wireSummary field is omitted from all Cell entities.
// HTTP catalog and CLI export both supply this to produce a consistent view.
WireSummaries []metadata.CellWireSummary
}
ExportOptions configures BuildDocument. All fields are optional. The required clock is passed as a positional parameter to BuildDocument.
type Filter ¶
type Filter struct {
Kinds []string
Layers []string
Cells []string
Include IncludeOptions
}
Filter is the projection applied to a ProjectMeta when building a Document. Empty slices mean "no filter on this dimension" (all entities pass). Cells, when non-empty, switches to focus mode: only the listed cells + their first-order neighbors (slices belongsTo, L0 deps, contract owners/clients) appear in the output.
type FilterEcho ¶
type FilterEcho struct {
Kinds []string `json:"kinds,omitempty" yaml:"kinds,omitempty"`
Layers []string `json:"layers,omitempty" yaml:"layers,omitempty"`
Cells []string `json:"cells,omitempty" yaml:"cells,omitempty"`
// Include is a sorted subset of AllIncludeTokens; omitted when empty.
Include []string `json:"include,omitempty" yaml:"include,omitempty"`
}
FilterEcho is the applied filter as it appears in Document.Query — i.e. with defaults already resolved (Include expanded into a sorted []string slice for wire stability). Allows clients to confirm which filters the server actually applied without parsing query strings themselves.
type IncludeOptions ¶
IncludeOptions selects which optional Document blocks BuildDocument will populate. Zero value = nothing optional included. Use AllIncluded() for full snapshots (CLI default).
func AllIncluded ¶
func AllIncluded() IncludeOptions
AllIncluded returns an IncludeOptions with every flag true. CLI default; HTTP default when no ?include= query parameter is provided.
func ParseIncludeTokens ¶
func ParseIncludeTokens(tokens []string) (IncludeOptions, error)
ParseIncludeTokens converts a sanitized token list into IncludeOptions. Caller is responsible for upstream allowlist validation (csvparam.ParseAllowed against AllIncludeTokens); this function only maps known tokens to fields and returns an error if any token is unrecognized (defensive double-check).
type JourneyPassCrit ¶
type JourneyPassCrit struct {
Text string `json:"text" yaml:"text"`
Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`
CheckRef string `json:"checkRef,omitempty" yaml:"checkRef,omitempty"`
}
JourneyPassCrit mirrors PassCriterionMeta on the wire.
type JourneySpec ¶
type JourneySpec struct {
Goal string `json:"goal,omitempty" yaml:"goal,omitempty"`
Lifecycle string `json:"lifecycle,omitempty" yaml:"lifecycle,omitempty"`
Owner CellSpecOwner `json:"owner" yaml:"owner"`
Cells []string `json:"cells,omitempty" yaml:"cells,omitempty"`
Contracts []string `json:"contracts,omitempty" yaml:"contracts,omitempty"`
PassCriteria []JourneyPassCrit `json:"passCriteria,omitempty" yaml:"passCriteria,omitempty"`
}
JourneySpec is Document.Entities[Kind=="Journey"].Spec.
type PackageDepsView ¶
type PackageDepsView struct {
Graph *kerneldepgraph.Graph `json:"graph,omitempty" yaml:"graph,omitempty"`
Error string `json:"error,omitempty" yaml:"error,omitempty"`
}
PackageDepsView is the package-level (Go import) view. Consumers determine state by checking: Graph != nil (ready) or Error != "" (error). Graph is the typed dep graph from tools/depgraph.Load — kept as a *kerneldepgraph.Graph pointer so wire shape stays canonical (kernel/depgraph owns its own MarshalJSON for determinism).
type Relation ¶
type Relation struct {
Type string `json:"type" yaml:"type"`
TargetRef string `json:"targetRef" yaml:"targetRef"`
}
Relation declares a directed link between two entities. TargetRef format: "{kind}/{name}" (lowercased kind, e.g. "slice/configcore.flagwrite").
type SliceSpec ¶
type SliceSpec struct {
BelongsToCell string `json:"belongsToCell" yaml:"belongsToCell"`
ConsistencyLevel string `json:"consistencyLevel" yaml:"consistencyLevel"`
// Lifecycle is the slice's governance maturity lifecycle (cellvocab.CellLifecycle),
// projected from SliceMeta.Lifecycle. Omitted when slice.yaml does not declare it.
// Note: distinct value set from ContractSpec.Lifecycle (maturity
// experimental|candidate|asset|maintenance|retired, not
// draft|active|deprecated).
Lifecycle string `json:"lifecycle,omitempty" yaml:"lifecycle,omitempty"`
ContractUsages []SliceSpecContractUsage `json:"contractUsages,omitempty" yaml:"contractUsages,omitempty"`
VerifyUnit []string `json:"verifyUnit,omitempty" yaml:"verifyUnit,omitempty"`
VerifyContract []string `json:"verifyContract,omitempty" yaml:"verifyContract,omitempty"`
AllowedFiles []string `json:"allowedFiles,omitempty" yaml:"allowedFiles,omitempty"`
}
SliceSpec is Document.Entities[Kind=="Slice"].Spec.
type SliceSpecContractUsage ¶
type SliceSpecContractUsage struct {
Contract string `json:"contract" yaml:"contract"`
Role string `json:"role" yaml:"role"`
}
SliceSpecContractUsage mirrors ContractUsageMeta on the wire.
type StatusBoardEntry ¶
type StatusBoardEntry struct {
JourneyID string `json:"journeyId" yaml:"journeyId"`
State string `json:"state" yaml:"state"`
Risk string `json:"risk,omitempty" yaml:"risk,omitempty"`
Blocker string `json:"blocker,omitempty" yaml:"blocker,omitempty"`
UpdatedAt string `json:"updatedAt" yaml:"updatedAt"`
}
StatusBoardEntry is a single row in the status board — one per journey. Risk and Blocker are redacted (cleared) for draft/planned entries before wire serialization so internal planning narratives do not leak.