Skip to content

feat(catalog): index bundles from linked Sources — replace the hardcoded renderer registry #111

Description

@pparage

Context

app/core/scenario_renderer/registry.py maps bundle grammar names
(<tier>/<subject>.<verb>[.<object>]) to physical bundle paths, and assigns each bundle a
BundleKind (VM / GROUP / XTIER / INFRA) — the call-site contract that decides where a
bundle may legally attach.

That registry is currently hardcoded. Every bundle the renderer can use is a literal
in Python. Adding a bundle to range42-playbooks does not make it usable by the renderer,
and a UI palette cannot list what actually exists.

Scope

Replace the hardcoded registry with an index built by scanning linked Sources.

The machinery mostly exists: /v1/catalog/sources (CRUD + refresh + entries) already
clones linked repos and walks them for manifests. The gap is that it indexes catalog
content (CVEs, containers, roles) and not bundles.

  1. Extend the scanner to index bundles — walk bundles/ in a linked source, derive
    the grammar name from the path, and derive kind from the bundle's own main.yml
    hosts:
    rather than requiring a descriptor file.
  2. Registry reads that index instead of its literals.
  3. Palette endpoint — group by subject, badge by verb/kind, so the UI can present
    what is genuinely available.
  4. Fork — copy a bundle across Sources into a writable project/personal Source, so
    range42-playbooks stays read-only/curated while user experiments live somewhere
    writable. The UI already has the Sources/PAT store and a git-provider client.
  5. Wire the existing guided bundle scaffolder (authoring.py) to write into that writable
    Source.

Deliberate non-goal: no per-bundle descriptor file

A bundle.r42.yml was considered and rejected. Of the five fields it would have carried:

  • name — is the path.
  • install_flagis not a property of a bundle. The same wazuh bundle is imported by
    9 scenarios with opposite default polarity; the flag belongs to the import site, and to
    the scenario's own feature_flags.yml.
  • kind — derivable from main.yml hosts: for 100% of the bundles the renderer uses.
    The scanner must comment-strip and scan all plays, not just the first. (~13 legacy
    create-vms-* / linux-ubuntu / ping bundles resist derivation, but they are
    retirement-bound.)
  • requires_vars — the renderer knows these per-kind.
  • ports — admin bundles declare them in catalog_try.yml or their own firewall play.
    CTF ports are the one real gap, and the fix belongs inside the bundle: promote the
    # TODO BUNDLE_PORTS_TCP comment that already sits in those bundles into a real key in
    their main.yml.

Principle: derive everything, author nothing. Anything a scanner can compute must not
become a file a human has to maintain in sync.

Naming

The <tier>/<subject>.<verb>[.<object>] grammar has a closed vocabulary — subjects
{software, system, network, credentials, repo, template, vm}, verbs {install, build, create,
configure, baseline, clone, bootstrap}; the verb is always the second segment; ctf/ is
exempt (identity-addressed by CVE). The verb list is the anti-anarchy lever: it is what
stops synonyms proliferating. The scanner should reject, or at least flag, a bundle whose
path does not parse.

Note

core/ is not yet renamed on disk, so the registry currently keeps an alias from grammar
name to the physical path. Delete the alias once the rename lands — the scanner should
not inherit it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions