catalog

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: MIT Imports: 9 Imported by: 0

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

View Source
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

View Source
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.

View Source
var AllKinds = []string{"Actor", "Assembly", "Cell", "Contract", "Journey", "Slice"}

AllKinds enumerates the entity kinds BuildDocument can emit.

View Source
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

func MarshalDocument(d Document, format string) ([]byte, error)

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 CellEdge

type CellEdge struct {
	From string `json:"from" yaml:"from"`
	To   string `json:"to"   yaml:"to"`
}

CellEdge is a directed dependency between two cells.

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

type IncludeOptions struct {
	Relations   bool
	StatusBoard bool
	CellDeps    bool
	PackageDeps bool
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL