Skip to main content

Registry CLI

registry-cli is a command-line tool for building and serving TS11-compliant Catalogue of Attestations sites. It discovers credential type metadata (VCTMs) from GitHub repositories and produces a static HTML site with a JSON API, OpenAPI specification, and optional JWS signing.

The public registry.siros.org is built and deployed using registry-cli. See the Registry Services overview for how the registry fits into the wallet ecosystem.

Quick Start with Docker​

The fastest way to run your own registry is using the published Docker image.

1. Create a sources file​

Create a directory for your registry and add a sources.yaml file that tells registry-cli where to find credential metadata:

mkdir -p my-registry/sources
cat > my-registry/sources/sources.yaml <<'EOF'
sources:
# Auto-discover repos tagged "vctm" on GitHub
- "github:topic/vctm"

# Or list specific repositories
- url: "git:https://github.com/sirosfoundation/demo-credentials.git"
branch: vctm
EOF

2. Run with Docker Compose​

Create a docker-compose.yml:

services:
registry:
image: ghcr.io/sirosfoundation/registry-cli:latest
ports:
- "8080:8080"
volumes:
- ./sources:/data/sources:ro
- ./output:/data/output
environment:
- GITHUB_TOKEN=${GITHUB_TOKEN:-}

Then start the registry:

export GITHUB_TOKEN=ghp_...   # needed for GitHub API access
docker compose up

The registry is now available at http://localhost:8080.

3. Run with Docker directly​

docker run -p 8080:8080 \
-v "$(pwd)/sources:/data/sources:ro" \
-v "$(pwd)/output:/data/output" \
-e GITHUB_TOKEN \
ghcr.io/sirosfoundation/registry-cli:latest

The container runs as an unprivileged user (uid 1000), so the bind-mounted output directory must be writable by it. The image also declares /data/tokens as a volume.

Static Site Generation​

To generate a static site (for hosting on GitHub Pages, Netlify, etc.) instead of running a live server:

docker run --rm \
-v ./sources:/data/sources:ro \
-v ./output:/data/output \
-e GITHUB_TOKEN \
ghcr.io/sirosfoundation/registry-cli:latest \
build \
--sources /data/sources/sources.yaml \
--output /data/output \
--base-url https://your-registry.example.com

The generated site will be in ./output/ and can be deployed to any static hosting provider.

Sources Configuration​

The sources.yaml file defines where registry-cli discovers credential metadata.

Source types​

FormatExampleDescription
GitHub topicgithub:topic/vctmAuto-discover all repos with the given topic
GitHub topic (scoped)github:topic/vctm?org=myorgDiscover within a specific GitHub organization
Git repositorygit:https://github.com/org/repo.gitExplicit git repository
Local directoryfile:///path/to/dirLocal filesystem path

Structured entries​

For more control, use structured source entries:

sources:
- url: "git:https://github.com/org/repo.git"
branch: main # override branch (default: repo default branch)
organization: "My Org" # override organization display name
layout: default # repository plugin layout (default: "default")
options: {} # layout-specific string options

Subfolder path targeting​

Added in v0.12.0

If a repository contains credentials in a subdirectory (rather than at the root), use the path field to restrict discovery to that subtree:

sources:
- url: "git:https://github.com/org/monorepo.git"
path: "credentials/production" # only scan this subfolder

This is useful for monorepos where credential definitions live alongside other project files. Only credential files within the specified path (and its subdirectories) are processed; everything else in the repository is ignored.

Path traversal (e.g. ../) is rejected for security.

Default settings​

defaults:
branch: main # default branch for all sources
sources:
- "git:https://github.com/org/repo.git"

Markdown Credential Authoring​

registry-cli converts Markdown files with vct: YAML front matter into credential metadata in multiple output formats. See the Custom SD-JWT Credential guide for a full end-to-end walkthrough.

Per-credential format override​

Added in v0.12.0

By default, registry-cli generates all registered output formats (SD-JWT VCTM, mDOC MDDL, W3C VCDM 2.0, and standalone JSON Schema) for every markdown credential. To generate only specific formats, add a formats field to the front matter:

---
vct: https://example.com/credentials/my-credential
formats: sd-jwt, w3c
---

Supported format names and aliases:

NameAliasesOutput file
vctmsd-jwt, sdjwt.vctm.json
mddlmdoc, mso_mdoc.mdoc.json
w3c–.vc.json
jsonschemajson-schema, schema.schema.json

When formats is omitted or empty, all registered formats are generated.

Nested object and array claims​

Added in v0.12.0

Claims that contain sub-fields (objects) or repeated items (arrays) can be expressed using Markdown sub-lists:

## Claims

- `name` (string): Full name [mandatory] [sd=always]
- `address` (object): Postal address [mandatory]
- `street` (string): Street address [mandatory]
- `city` (string): City [mandatory]
- `postal_code` (string): Postal code
- `country` (string): Country code
- `previous_addresses` (array): Previous addresses
- `street` (string): Street address
- `city` (string): City

The container type determines how children are represented in each output format:

Container typeVCTM (SD-JWT)mDOC (MDDL)W3C (JSON Schema)
objectNested claim paths (e.g. ["address", "street"])Dot-notation keys (e.g. address.street)Nested properties in JSON Schema
arrayArray paths with null index (e.g. ["previous_addresses", null, "city"])Dot-notation keys (e.g. previous_addresses.city)items schema with properties

Schema-meta: TS11 Governance Metadata​

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

Required Fields​

Every credential must declare its governance properties:

FieldValuesDescription
attestation_losiso_18045_high, iso_18045_moderate, iso_18045_enhanced-basic, iso_18045_basicAttestation Level of Surety per ISO 18045. Friendly aliases (high, moderate, enhanced-basic, basic, substantial, low) are normalized automatically.
binding_typekey, biometric, claim, noneHow the credential is bound to the holder. Aliases: cnf → key, holder → key.

Optional Fields​

FieldTypeDefaultDescription
versionstring"0.1.0"Credential schema version (semver). Can also be derived from git tags.
rulebook_uristring(auto-detected)URL to attestation rulebook. Registry-cli auto-generates this from co-located rulebook.md if present.
trusted_authoritiesarray(empty)Trust framework references per TS11 §4.3.3. See Full Example with Trusted Authorities below.

Auto-Generated Fields​

The following fields are inferred automatically during build and should not be set in the YAML:

FieldSource
idUUID v5 deterministically derived from organization + slug
supportedFormatsAuto-detected from co-located files: .vctm.json → dc+sd-jwt, .mdoc.json → mso_mdoc, .vc.json → jwt_vc_json
schemaURIsGenerated from supportedFormats, using the registry base URL

Minimal Example​

attestation_los: iso_18045_high
binding_type: key

Full Example with Trusted Authorities​

attestation_los: iso_18045_high
binding_type: key
version: "1.0.0"
rulebook_uri: https://example.com/credentials/my-credential/rulebook.html
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 Reference​

FieldTypeRequiredDescription
framework_typestringYesIdentifier for the trust framework (e.g., etsi_tl, eidas, custom-scheme)
valuestringYesTrust list URL or authority endpoint
is_lotebooleanNoWhether this is a List of Trusted Entities (LOTE)
trust_mark_idstringNoURI identifying the trust mark
trust_mark_issuersarray of stringsNoURLs of entities authorized to issue this trust mark

File Placement​

Place the .schema-meta.yaml file alongside your credential files with a matching base name:

credentials/
├── my-credential.md (markdown source or pre-built)
├── my-credential.schema-meta.yaml (TS11 metadata — required)
├── 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 (HTML pages, credential listings)
  • ❌ Are excluded from TS11 API responses (/api/v1/schemas.json)

This allows incremental migration to TS11 compliance. To make a credential TS11-compliant, add a .schema-meta.yaml file and rebuild the registry.

CLI Reference​

registry-cli build​

Build a static registry site from credential sources.

FlagDefaultDescription
--sourcessources.yamlPath to sources manifest
--outputdistOutput directory
--base-urlhttps://registry.siros.orgBase URL for generated links
--templates—Path to custom template overrides
--static—Path to custom static assets

registry-cli serve​

Build and serve the registry with a live API. Inherits all build flags plus:

FlagDefaultDescription
--addr127.0.0.1Bind address
--port8080Listen port
--pkcs11-uri—PKCS#11 URI for JWS signing
--key-label—PKCS#11 key label
--issuer—JWT issuer claim
--jku—JWS Key URL header
--base-url default differs from build

serve redeclares --base-url with its own empty default rather than reusing build's https://registry.siros.org — when omitted, it falls back to http://<addr>:<port>. Pass it explicitly if you're serving behind a different public hostname (e.g. a reverse proxy).

serve also mounts two live, query-filterable endpoints alongside the static /api/v1/*.json files described below: GET /api/v1/schemas and GET /api/v1/attributes (no .json suffix), supporting query parameters such as attestationLoS, bindingType and supportedFormats.

registry-cli sign​

Sign API responses with JWS (RFC 7515). Supports ephemeral keys, SoftHSM, and hardware HSMs.

FlagDefaultDescription
--input(required)Input directory with JSON files
--pattern*.jsonGlob pattern for files to sign
--pkcs11-uri—PKCS#11 URI (ephemeral key if omitted)
--key-labelregistry-signingHSM key label
--issuer—JWT issuer; with the ephemeral signer an empty value becomes registry-cli, with --pkcs11-uri it stays empty
--jku—JWS Key URL (jku header)
--jwks-output—Path for JWKS public key file
--aggregate—Path to write the aggregate JWS (for the schemas list)
--previous-jwks—Previous JWKS file, for key rotation
--key-retention720hHow long previous signing keys stay in the JWKS after rotation

Custom Templates​

Override the default HTML templates by providing a --templates directory. Templates use Go's html/template syntax. See the registry.siros.org templates for examples.

API Output​

The generated registry includes a TS11-compliant JSON API:

EndpointDescription
/api/v1/schemas.jsonAll credential schemas
/api/v1/schemas/<id>.jsonIndividual credential schema
/api/v1/orgs.jsonIndex of contributing organizations (SIROS extension, not part of TS11 — see below)
/api/v1/orgs/<org>/schemas.jsonTS11-compliant schemas for one organization (SIROS extension, not part of TS11 — see below)
/api/v1/attributes.jsonCatalogue of attributes
/api/v1/openapi.yamlOpenAPI 3.1 specification
/.well-known/vctm-registry.jsonVCTM registry discovery
/.well-known/jwks.jsonPublic signing keys (when signing is enabled) — root-level .well-known, not under /api/v1/

When JWS signing is enabled, all JSON responses are also available as .jwt files (JWS compact serialization).

Organization-scoped filtering​

Added in v0.17.0

/api/v1/orgs/<org>/schemas.json returns the same shape as /api/v1/schemas.json, filtered to one organization's TS11-compliant schemas. /api/v1/orgs.json lists every organization known to the registry instance, with a schemaCount and a link to its schemasURL.

This is a SIROS-specific extension, not part of the normative ETSI TS11 API: TS11 has no "organization" concept, and its SchemaMeta JSON Schema sets additionalProperties: false at the top level, so adding an org/repo field directly to the object would break TS11 compliance validation for every credential. Filtering is expressed as a separate resource collection instead — every schema object returned from an org-scoped list is byte-for-byte identical to the one served from the global list, so this has no effect on TS11 conformance. It works identically on static hosting (GitHub Pages) and registry-cli serve.

There is no static resource for an arbitrary set of organizations — fetch each organization's list separately and merge client-side.

Environment Variables​

VariableDescription
GITHUB_TOKENGitHub personal access token for repository discovery and cloning
PKCS11_PINPKCS#11 PIN, used when none is given in the --pkcs11-uri

Installation from Source​

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

Or build locally:

git clone https://github.com/sirosfoundation/registry-cli.git
cd registry-cli
make build