IssuerDirectory

IssuerDirectory is the Stella Ops trust registry for CSAF publishers and VEX statement issuers — it stores issuer metadata, signing keys, and per-tenant trust weights that Concelier/Excititor and VexLens consult when verifying signed advisory and VEX content.

Status: Implemented (source relocated by Sprint 216) Source: src/Authority/StellaOps.IssuerDirectory/ (previously src/IssuerDirectory/) Owner: Authority domain (Identity & Trust)

Latest updates (2026-04-16)

Purpose

IssuerDirectory maintains a trust registry of CSAF publishers and VEX statement issuers. It stores issuer metadata, issuer signing keys (with rotation/revocation lifecycle), and per-tenant trust overrides (weights) for upstream vulnerability advisories and VEX statements. Consumers (notably Concelier/Excititor and VexLens) read issuer keys and trust weights when verifying signed advisory/VEX content.

Domain ownership

As of Sprint 216, IssuerDirectory source is owned by the Authority domain. The runtime service identity, container, and database schema remain independent. Schema isolation from AuthorityDbContext is a deliberate security feature (blast-radius containment between issuer-metadata handling and authentication internals).

See ../authority/architecture.md(sections 21.1–21.4) for schema ownership and the no-merge rationale.

Components

The module is split across the Authority tree:

Service host (src/Authority/StellaOps.IssuerDirectory/):

Libraries (src/Authority/__Libraries/):

HTTP API

All routes are under the /issuer-directory/issuers group and require an Authority token carrying the policy scope below (X-StellaOps-TenantId header selects the tenant; an optional X-StellaOps-Reason header is recorded in the audit trail). Most read routes accept ?includeGlobal=true (default true on issuer reads) to fold in global/system-seed issuers.

Method & pathOperationRequired policy / scope
GET /issuer-directory/issuersList issuersReader / issuer-directory:read
GET /issuer-directory/issuers/{id}Get issuerReader / issuer-directory:read
POST /issuer-directory/issuersCreate issuerWriter / issuer-directory:write
PUT /issuer-directory/issuers/{id}Update issuerWriter / issuer-directory:write
DELETE /issuer-directory/issuers/{id}Delete issuerAdmin / issuer-directory:admin
GET /issuer-directory/issuers/{issuerId}/keysList issuer keysReader / issuer-directory:read
POST /issuer-directory/issuers/{issuerId}/keysAdd keyWriter / issuer-directory:write
POST /issuer-directory/issuers/{issuerId}/keys/{keyId}/rotateRotate keyWriter / issuer-directory:write
DELETE /issuer-directory/issuers/{issuerId}/keys/{keyId}Revoke keyAdmin / issuer-directory:admin
GET /issuer-directory/issuers/{issuerId}/trustGet effective trustReader / issuer-directory:read
PUT /issuer-directory/issuers/{issuerId}/trustSet tenant trust overrideWriter / issuer-directory:write
DELETE /issuer-directory/issuers/{issuerId}/trustDelete trust overrideAdmin / issuer-directory:admin

Health/diagnostics: GET /healthz (liveness), GET /readyz (readiness), and a build-info endpoint are mapped anonymously. OpenAPI is exposed via MapOpenApi.

Authorization

Endpoints bind to three named policies (IssuerDirectoryPolicies): IssuerDirectory.Reader, IssuerDirectory.Writer, IssuerDirectory.Admin. When IssuerDirectory:Authority:Enabled=true (default), each policy is registered via AddStellaOpsScopePolicy against the configured ReadScope / WriteScope / AdminScope (defaults issuer-directory:read, issuer-directory:write, issuer-directory:admin — see StellaOpsScopes.IssuerDirectoryRead/Write/Admin). The framework handler honours configured BypassNetworks (e.g. ISSUERDIRECTORY__AUTHORITY__BYPASSNETWORKS__0). When Authority is disabled (local/test), all three policies allow anonymous access.

Key types

Issuer key requests carry a type string parsed into the IssuerKeyType enum:

(The startup-migration SQL key_type CHECK constraint lists a broader value set — ed25519, x509, dsse, kms, hsm, fido2 — but only the three enum members above are accepted by the WebService today.) Trust override weights are constrained to the range [-10, 10].

Configuration

See etc/issuer-directory.yaml.sample for the full sample. The binding section is IssuerDirectory and the environment prefix is ISSUERDIRECTORY_ (double-underscore for nesting, e.g. ISSUERDIRECTORY__PERSISTENCE__POSTGRESCONNECTIONSTRING).

Key settings:

Persistence

PostgreSQL schema (default issuer), auto-migrated on startup via AddStartupMigrations (module IssuerDirectory.Persistence, embedded Migrations/001_initial_schema.sql). Tables:

In-memory repositories exist for the Testing environment only. Unified audit events are also emitted to Timeline via AddAuditEmission.

Observability

Meter StellaOps.IssuerDirectory exposes:

Dependencies

Consumers: