Skip to content

About

No description or website provided.

Topics

Resources

Security policy

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

registry-cli

CI Security OpenSSF Scorecard License

A CLI tool for building TS11-compliant Catalogue of Attestations sites from credential schemas discovered across GitHub repositories.

Overview

registry-cli discovers credential type metadata (VCTMs) from configured source repositories, validates them against the TS11 schema-meta model, and produces a static site with:

  • Per-credential detail pages with TS11 metadata
  • Rendered rulebooks (from co-located rulebook.md)
  • A JSON API (/api/v1/schemas.json) with per-schema and per-organization endpoints
  • An OpenAPI specification
  • Optional JWS signing of all API responses

Installation

go install github.com/sirosfoundation/registry-cli/cmd/registry-cli@latest

Usage

Build a site

registry-cli build \
  --sources sources.yaml \
  --output dist \
  --base-url https://registry.siros.org \
  --templates templates-go \
  --static static

Sign API responses (requires PKCS#11 or dev key)

registry-cli sign \
  --input dist/api/v1 \
  --key-mode dev

Sources manifest

The sources.yaml file declares where to find credential repositories:

defaults:
  branch: vctm

sources:
  # Auto-discover by GitHub topic
  - "github:topic/vctm?org=sirosfoundation"

  # Explicit repository
  - "git:https://github.com/org/repo.git"

  # Local directory with explicit organization label
  - url: "file:///path/to/local/credentials"
    organization: "MyOrg"

  # Remote repo with custom org label
  - url: "git:https://github.com/other/repo.git"
    organization: "CustomLabel"

Source entries can be plain strings or structs with url and optional organization fields. Local (file://) sources default to organization "Local" if no label is provided.

Layout plugins

Each source entry may specify a layout field to select a repo plugin that understands the repository's directory structure. If omitted, the default plugin is used.

sources:
  # Default layout (omitted or explicit)
  - url: "git:https://github.com/sirosfoundation/demo-credentials.git"
    branch: vctm

  # EUDI-style rulebook catalog
  - url: "git:https://github.com/webuild-consortium/webuild-attestation-rulebooks-catalog.git"
    organization: "webuild-consortium"
    layout: "rulebook-catalog"
    options:
      attestation_los: "iso_18045_high"
      binding_type: "key"

Source entry fields

Field Type Default Description
url string (required) Source URL (git:, file://, or meta-source like github:topic/vctm)
organization string (inferred from URL) Organization label for grouping
branch string manifest default Git branch to clone
path string (repo root) Restrict discovery to a subdirectory
layout string "default" Repo plugin name (see below)
options map (empty) Plugin-specific key-value configuration

Available plugins

default

Handles the standard SIROS credential layout with co-located files per credential:

<slug>.schema-meta.yaml   (TS11 governance — required for TS11 API)
<slug>.vctm.json          (SD-JWT VC Type Metadata)
<slug>.mdoc.json          (mDOC metadata)
<slug>.vc.json            (W3C VC schema)
rulebook.md               (co-located governance document)

Also supports:

  • Markdown files with vct: YAML front matter (auto-converted to all formats)
  • Legacy bare .vctm or .json files without schema-meta
rulebook-catalog

Handles EUDI ARF-style attestation rulebook catalog repositories such as webuild-attestation-rulebooks-catalog and eudi-doc-attestation-rulebooks-catalog.

Expected directory structure:

data-schemas/
  sd-jwt/<name>-sd-jwt.json    (JSON Schema 2020-12 validation definitions)
  mdoc/<name>-mdoc.json        (mDOC validation schemas)
rulebooks/
  rb-<slug>/README.md          (EUDI-template governance document)

Discovery rule:

A credential type exists for every slug that has at least one data schema: data-schemas/sd-jwt/<name>-sd-jwt.json and/or data-schemas/mdoc/<name>-mdoc.json. The slug is the file name without its -sd-jwt.json / -mdoc.json suffix and without a leading dsNNN- (case is kept: ds009-eInvoice becomes eInvoice). A type with an SD-JWT VC schema follows the SD-JWT flow below, with its mdoc schema attached as a second format. A type with only an mdoc schema follows the mdoc-only flow.

What it does for a type with an SD-JWT VC schema:

  1. Extracts vct from properties.vct.const or properties.vct.examples[0]
  2. Converts JSON Schema properties → VCTM claims:
    • required array → mandatory: true
    • Descriptions with "selectively disclosable" → sd: "always"
    • Property names → display labels (snake_case → Title Case)
  3. Maps rulebooks/rb-<slug>/README.md as the governance rulebook
  4. Attaches the matching mDOC schema from data-schemas/mdoc/ as a second format
  5. Normalizes legacy dsNNN- filename prefixes (e.g., ds002-pid → pid)
  6. Discovers card and logo artwork per credential and publishes it with the site (see below)

What it does for an mdoc-only type:

  1. Publishes the mdoc schema as <org>/<slug>.mdoc.json
  2. Publishes the TS11 entry with supportedFormats: ["mso_mdoc"], a schemaURIs entry for the mdoc file and, with the source options (attestation_los, binding_type, version), the same governance metadata as the other types; rulebookURI points at the rendered rulebook when rulebooks/rb-<slug>/README.md exists
  3. Publishes the catalogue entry (.well-known/vctm-registry.json, with the .mdoc.json file as its metadata link) and the credential detail page, which shows the display name, description, document type, format downloads, rulebook link, source and the raw mdoc schema
  4. Publishes the artwork found by the asset conventions below through the same validated asset path (<org>/<slug>-svg_template.svg, -svg_template_dark.svg, -logo.*)
  5. Skips a type whose mdoc schema is not valid JSON, logs a warning and continues with the other types

A type without VCTM has no claims, so it appears in the TS11 schemas API, the catalogue and the organisation page, while the attribute catalogue lists the claims of VCTM-based types.

mdoc schema fields that are read:

Field Used for
title display name on the detail page, organisation page and catalogue entry (the slug when absent)
description description on the detail page and catalogue entry
docType document type shown on the detail page: a top-level docType string, properties.docType.const, or the first docType property with a const found in the document (WE BUILD schemas declare it under $defs)

Asset conventions:

Each credential <slug> may carry artwork. The plugin looks in these places, first match wins per asset:

Asset Preferred location Also found in
Card (light) assets/<slug>/card.svg data-schemas/sd-jwt/<slug>-card.svg, display/<slug>-card.svg, display/<slug>.svg
Card (dark) assets/<slug>/card-dark.svg
Logo assets/<slug>/logo.svg or logo.png data-schemas/sd-jwt/<slug>-logo.{svg,png}, display/<slug>-logo.{svg,png}

Published URLs: every asset is copied next to the credential's VCTM and the VCTM references it by absolute URL (--base-url with a trailing slash trimmed):

<out>/<org>/<slug>-svg_template.svg        https://<base-url>/<org>/<slug>-svg_template.svg       (card.svg)
<out>/<org>/<slug>-svg_template_dark.svg   https://<base-url>/<org>/<slug>-svg_template_dark.svg  (card-dark.svg)
<out>/<org>/<slug>-logo.svg|png            https://<base-url>/<org>/<slug>-logo.svg|png           (logo.svg|png)

The VCTM lists the cards under display[].rendering.svg_templates[] (light and dark colour scheme) and the logo under display[].rendering.simple.logo. Published asset types are svg, png, jpg, jpeg and webp. Credential format files keep their own names and content; an asset is always written under its <slug>-<key> name.

SVG cards are checked before publication, see SVG and Image URI Validation. A card that fails a check is skipped with a logged warning and its reference is removed from the VCTM, so the published VCTM contains working links only.

Plugin options:

Option Description
attestation_los Default TS11 Level of Surety for all credentials in this source
binding_type Default TS11 binding type for all credentials in this source
version Default schema version

Without attestation_los and binding_type, credentials will appear on the site but not in the TS11 API (same as publishing without schema-meta).

Writing a custom plugin

Plugins implement the repoplugin.Plugin interface:

package repoplugin

type Plugin interface {
    Name() string
    Description() string
    Discover(ctx Context) ([]DiscoveredCredential, error)
}

Plugins self-register via init() and are compiled in by importing the package:

import _ "github.com/sirosfoundation/registry-cli/pkg/repoplugin/myplugin"

See pkg/repoplugin/rulebookcatalog/ for a complete example.

Schema-meta: TS11 Governance Metadata

Each credential must have a co-located .schema-meta.yaml file (or .schema-meta.json) providing TS11 Catalogue of Attestations compliance metadata. Only two fields are required; all others are inferred automatically.

Required Fields

Field Values Description
attestation_los iso_18045_high, iso_18045_moderate, iso_18045_enhanced-basic, iso_18045_basic Attestation Level of Surety per ISO 18045. Friendly aliases (high, moderate, enhanced-basic, basic, substantial, low) are normalized automatically.
binding_type key, biometric, claim, none How the credential is bound to the holder. Friendly aliases (cnf → key, holder → key) are normalized.

Optional Fields

Field Type Default Description
version string "0.1.0" Schema version (semver). Can also be derived from git tags.
rulebook_uri string (auto-generated) URL to attestation rulebook. If omitted, registry-cli auto-detects from co-located rulebook.md.
trusted_authorities array (empty) Trust framework references per TS11 §4.3.3. See Trusted Authorities below.

Auto-Generated Fields (do NOT set these)

The following fields are inferred automatically at build time:

Field Source
id UUID v5 deterministically derived from org/slug
supportedFormats Detected from co-located files: .vctm.json → dc+sd-jwt, .mdoc.json → mso_mdoc, .vc.json → jwt_vc_json
schemaURIs Generated from supportedFormats and registry base URL

Minimal Example

attestation_los: iso_18045_high
binding_type: key

Full Example

attestation_los: iso_18045_high
binding_type: key
version: "1.0.0"
rulebook_uri: https://example.com/credentials/my-credential/rulebook
trusted_authorities:
  - framework_type: etsi_tl
    value: "https://tl.etsi.org/export/trustlist.xml"
    is_lote: false
  - framework_type: eidas
    value: "https://eidas.ec.europa.eu"
    is_lote: false
    trust_mark_id: "https://eidas.ec.europa.eu/markers/high"
    trust_mark_issuers:
      - "https://issuer.example.com"

Trusted Authorities

Trusted authority entries define governance frameworks and trust marks:

Field Type Required Description
framework_type string Yes Identifier for the trust framework (e.g., etsi_tl, eidas, custom-scheme)
value string Yes Trust list URL or authority endpoint
is_lote boolean No Whether this is a List of Trusted Entities (LOTE)
trust_mark_id string No URI identifying the trust mark
trust_mark_issuers array of strings No URLs of entities authorized to issue this trust mark

File Placement

Place the .schema-meta.yaml file in the same directory and with the same base name as your credential files:

credentials/
├── my-credential.md              (markdown source)
├── my-credential.schema-meta.yaml (TS11 metadata)
├── my-credential.vctm.json       (auto-generated or pre-built)
├── my-credential.mdoc.json       (auto-generated or pre-built)
└── my-credential.vc.json         (auto-generated or pre-built)

Publishing Without Schema-Meta

Credentials discovered without a .schema-meta.yaml file appear in the human-readable site but are excluded from TS11 API responses (/api/v1/schemas.json). This allows incremental migration to TS11 compliance.

Organization-Scoped API Filtering

registry-cli build also writes /api/v1/orgs.json (an index of contributing organizations) and /api/v1/orgs/<org>/schemas.json (that organization's TS11-compliant schemas, same shape as /api/v1/schemas.json). This is a SIROS extension, not part of the normative TS11 API — TS11 has no "organization" concept, and its SchemaMeta JSON Schema forbids additional properties, so filtering is expressed as a separate resource collection rather than a new field on the schema object. Every schema returned from an org-scoped list is identical to the one served from the global list.

There is no static resource for a set of organizations — fetch each one's list and merge client-side.

Packages

Package Purpose
pkg/discovery Source resolution, GitHub topic search, repo cloning
pkg/repoplugin Repo layout plugin interface and registry
pkg/repoplugin/defaultlayout Default plugin: co-located schema-meta + format files
pkg/repoplugin/rulebookcatalog EUDI-style catalog plugin: JSON Schema → VCTM conversion
pkg/schemameta TS11 schema-meta parsing, inference, validation
pkg/assets SVG validation and publication of layout plugin assets
pkg/render HTML template rendering, markdown conversion
pkg/jwssign JWS signing (dev, SoftHSM, YubiHSM) via PKCS#11
pkg/apihandler TS11 REST API with filtering, pagination, JWS-signed responses
pkg/ts11compliance TS11 specification compliance test suite
pkg/mdcred Markdown-based credential conversion

Output Formats

registry-cli convert transforms Markdown credential definitions into the following formats:

Format Aliases Output file Description
vctm sd-jwt, sdjwt .vctm.json SD-JWT VC Type Metadata
mddl mdoc, mso_mdoc .mdoc.json mDOC MDDL (ISO 18013-5)
w3c jwt_vc_json .vc.json W3C VCDM 2.0 credential schema
jsonschema json-schema, schema .schema.json Standalone JSON Schema (draft-07)

By default all formats are generated. Use --format or formats: front matter to select specific formats.

Serve (development)

registry-cli serve --sources sources.yaml --output dist --port 8080

Docker

# Build and run with docker compose
docker compose up --build

# Or build the image directly
docker build -t registry-cli:latest .
docker run -p 8080:8080 -v ./sources:/data/sources:ro registry-cli:latest

Security

Markdown and Content Sanitization

registry-cli processes potentially untrusted content from external credential repositories (rulebooks, VCTM metadata). All user-generated content is sanitized to prevent XSS attacks:

Rulebook Markdown Sanitization

Attestation rulebooks are converted from Markdown to HTML and sanitized using a strict content security policy:

Allowed elements: paragraphs, headings, bold, italic, links, tables, lists, code blocks, blockquotes

Blocked elements: script tags, objects, embeds, SVGs, style tags, iframe, form

Blocked attributes: event handlers (onclick, onerror, etc.), style attributes, javascript: URIs, data: URIs

Example attack prevention:

<!-- Blocked: scripts are stripped -->
<script>alert('xss')</script>

<!-- Blocked: event handlers are removed -->
<img src=x onerror="alert(1)">

<!-- Blocked: style attributes prevent CSS injection -->
<p style="position:fixed;...">Overlay</p>

<!-- Blocked: javascript: URIs are filtered -->
[Click me](javascript:alert(1))

<!-- Allowed: safe links are preserved -->
[Documentation](https://example.com/docs)

SVG and Image URI Validation

Credential metadata (VCTM) may reference images for logos and backgrounds:

  • Allowed: https:// and http:// URIs pointing to external image files
  • Blocked: data: URIs (inline data), blob: URIs, javascript: protocols, file:// URIs

Invalid image URIs are silently removed from rendered credential pages. The implementation is in pkg/render/vctm.go::SanitizeVCTM().

Published SVG assets. The registry serves SVG cards and logos discovered by layout plugins from its own origin, so every SVG is validated with a real XML tokenizer (encoding/xml, pkg/assets/svg.go::ValidateSVG) before it is published. A card is published when it:

  • is a well-formed XML document with an <svg> root element of at most 2 MiB;
  • contains only static graphic elements (no <script>, <foreignObject>, <iframe>, <object>, <embed>);
  • carries no event-handler attributes (onload, onclick, any on*);
  • uses href, xlink:href and src only for same-document #id fragments, which also covers <use> of external documents;
  • contains no javascript: or data: URIs, and no external url(...), @import or entity declarations in style elements and attributes.

A rejected card is skipped with a logged warning that names the file and the reason, and its reference is removed from the VCTM. Absolute https:// and http:// asset URIs pass SanitizeVCTM, so published assets appear on the rendered credential pages.

Testing

Comprehensive security tests verify sanitization effectiveness:

go test ./pkg/render -run "Sanitize" -v

Tests cover:

  • XSS via script tags and event handlers
  • CSS injection via style attributes
  • SVG and object attacks
  • Data URI attacks
  • JavaScript protocol handlers
  • Safe content preservation (links, tables, lists)

Recommendations for Repository Maintainers

When publishing credentials with rulebooks:

  1. Avoid HTML in Markdown: Write rulebooks using standard Markdown syntax
  2. Don't embed SVGs: Reference SVGs via URLs only, never inline
  3. Use safe links: Only use https:// links to trusted documentation
  4. No styling: Don't use HTML style attributes or CSS; format using Markdown
  5. No JavaScript: Avoid any JavaScript references or data URIs

Example safe rulebook structure:

# PID Attestation Rulebook

## Overview
This rulebook governs Personal ID credentials.

- [Framework documentation](https://example.com/pid-framework)
- [Technical specifications](https://example.com/pid-spec)

## Requirements
- Attestation LoS: high
- Binding type: key

## See also
- [PID Rulebook v1.5](https://example.com/pid-rulebook-1.5)

Testing

make test              # Unit tests
make test-softhsm      # Include SoftHSM PKCS#11 integration tests
make coverage           # Coverage report
make lint               # Run linter

License

See LICENSE.

About

No description or website provided.

Topics

Resources

Security policy

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages