OpenAPI Aggregation

This document describes how the StellaOps Gateway aggregates OpenAPI documentation from connected microservices into per-service and merged specifications.

Overview

The Gateway uses fan-out aggregation for OpenAPI. It enumerates HELLO-registered services from router state, fetches each service’s own OpenAPI document over the internal .stella-ops.local network, and then serves both a per-service envelope and a merged OpenAPI 3.1 document. This provides:

OAPI-059-001 Per-Service OpenAPI Audit (2026-05-18)

This audit records the per-service OpenAPI state after OAPI-059-001. It is intentionally limited to endpoint publication and does not redesign the gateway aggregation flow.

Service hostProgram pathEmits OpenAPI after this pass?Path(s)OAPI-059-001 result
AdvisoryAI.WebServicesrc/AdvisoryAI/StellaOps.AdvisoryAI.WebService/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate.
OpsMemory.WebServicesrc/AdvisoryAI/StellaOps.OpsMemory.WebService/Program.csYes/openapi/v1.json, /swagger/v1/swagger.json, /swaggerAdded default AddOpenApi/MapOpenApi; un-gated existing Swagger middleware.
Attestor.WebServicesrc/Attestor/StellaOps.Attestor/StellaOps.Attestor.WebService/Program.csYes/openapi/v1.jsonAdded default AddOpenApi/MapOpenApi; no new package was required.
Signer.WebServicesrc/Attestor/StellaOps.Signer/StellaOps.Signer.WebService/Program.csYes/openapi/v1.jsonAdded default AddOpenApi/MapOpenApi; no new package was required.
Authoritysrc/Authority/StellaOps.Authority/StellaOps.Authority/Program.csYes/.well-known/openapiExisting custom static-document provider; unchanged in this pass.
IssuerDirectory.WebServicesrc/Authority/StellaOps.IssuerDirectory/StellaOps.IssuerDirectory.WebService/Program.csYes/openapi/v1.jsonAdded default AddOpenApi/MapOpenApi; no new package was required.
BinaryIndex.WebServicesrc/BinaryIndex/StellaOps.BinaryIndex.WebService/Program.csYes/openapi/v1.json, /swagger/v1/swagger.json, /swaggerAdded the central Microsoft.AspNetCore.OpenApi package and default AddOpenApi/MapOpenApi; retained un-gated Swagger middleware.
Symbols.Serversrc/BinaryIndex/StellaOps.Symbols.Server/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate.
Concelier.WebServicesrc/Concelier/StellaOps.Concelier.WebService/Program.csYes/.well-known/openapi, /swagger/v1/swagger.json when Swagger is enabledExisting custom provider; unchanged in this pass.
Excititor.WebServicesrc/Concelier/StellaOps.Excititor.WebService/Program.csYes/.well-known/openapi, /openapi/excititor.jsonExisting custom provider; unchanged in this pass.
Doctor.WebServicesrc/Doctor/StellaOps.Doctor.WebService/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate.
EvidenceLocker.WebServicesrc/EvidenceLocker/StellaOps.EvidenceLocker/StellaOps.EvidenceLocker.WebService/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate. Local-only restore/build now passes with the existing package cache.
ExportCenter.WebServicesrc/ExportCenter/StellaOps.ExportCenter/StellaOps.ExportCenter.WebService/Program.csYes/openapi/v1.json, /.well-known/openapi, /openapi/export-center.json, /openapi/export-center.yamlRemoved default MapOpenApi gate; existing discovery endpoints unchanged.
Findings.Ledger.WebServicesrc/Findings/StellaOps.Findings.Ledger.WebService/Program.csYes/.well-known/openapiRestricted by OAPI-059-001 worker scope; existing static provider unchanged.
RiskEngine.WebServicesrc/Findings/StellaOps.RiskEngine.WebService/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate.
Integrations.WebServicesrc/Integrations/StellaOps.Integrations.WebService/Program.csYes/openapi/v1.json, /swagger/v1/swagger.json, /swaggerAdded default AddOpenApi/MapOpenApi; un-gated existing Swagger middleware.
PacksRegistry.WebServicesrc/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.WebService/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate.
Scheduler.WebServicesrc/JobEngine/StellaOps.Scheduler.WebService/Program.csYes/openapi/v1.jsonAdded the central Microsoft.AspNetCore.OpenApi package and default AddOpenApi/MapOpenApi; local no-dependency build passed.
Notifier.WebServicesrc/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Program.csNo active web targetNone in the active matrixHistorical source remains in the solution, but services-matrix.env marks notifier-web as merged into notify-web; only notifier-worker is an active matrix target.
Notify.WebServicesrc/Notify/StellaOps.Notify.WebService/Program.csYes/openapi/v1.jsonRemoved the local no-op OpenApiExtensions.WithOpenApi shim and stale .WithOpenApi() group calls that collided with or errored against the framework extension; added the central Microsoft.AspNetCore.OpenApi package plus default AddOpenApi/MapOpenApi; local restore/build passed.
Platform.WebServicesrc/Platform/StellaOps.Platform.WebService/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate; Platform conformance skip removed.
ReachGraph.WebServicesrc/ReachGraph/StellaOps.ReachGraph.WebService/Program.csYes/openapi/v1.json, /swagger/v1/swagger.json, /swaggerAdded default AddOpenApi/MapOpenApi; un-gated existing Swagger middleware.
Remediation.WebServicesrc/Remediation/StellaOps.Remediation.WebService/Program.csSource wired; not active matrix/publish target/openapi/v1.json if separately hostedThe project exists and was wired earlier in Sprint 059, but there is no active remediation-web entry in services-matrix.env or build-service-publish.sh; do not count it in live rebuild proof until a service owner reactivates it.
Replay.WebServicesrc/Replay/StellaOps.Replay.WebService/Program.csYes/.well-known/openapiExisting custom provider; unchanged in this pass.
SbomServicesrc/SbomService/StellaOps.SbomService/Program.csYes/openapi/v1.jsonAdded the central Microsoft.AspNetCore.OpenApi package and default AddOpenApi/MapOpenApi; local no-dependency build passed.
Scanner.WebServicesrc/Scanner/StellaOps.Scanner.WebService/Program.csYes/openapi/v1.json when Microsoft.AspNetCore.OpenApi is presentExisting MapOpenApiIfAvailable reflection helper; unchanged in this pass.
Tester.WebServicesrc/Tester/StellaOps.Tester.WebService/Program.csNoNoneExplicitly forbidden for this worker; left for Tester-owned sprint.
Timeline.WebServicesrc/Timeline/StellaOps.Timeline.WebService/Program.csYes/openapi/v1.json, /swagger/v1/swagger.json, /swaggerActive matrix target timeline-web; includes the merged timeline-indexer endpoints and worker responsibilities.
TimelineIndexer.WebServicesrc/Timeline/StellaOps.TimelineIndexer.WebService/Program.csMerged into timeline-webNone as an active matrix targetHistorical project/source remains, but services-matrix.env comments out timeline-indexer-web and timeline-indexer-worker as merged into timeline-web; rebuild and live proof should use timeline-web.
Unknowns.WebServicesrc/Unknowns/StellaOps.Unknowns.WebService/Program.csYes/openapi/v1.json, /swagger/v1/swagger.json, /swaggerAdded default AddOpenApi/MapOpenApi; un-gated existing Swagger middleware.
VexHub.WebServicesrc/VexHub/StellaOps.VexHub.WebService/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate.
VexLens.WebServicesrc/VexLens/StellaOps.VexLens.WebService/Program.csYes/openapi/v1.jsonRemoved Environment.IsDevelopment() gate.
Workflow.WebServicesrc/Workflow/StellaOps.Workflow.WebService/Program.csYes/openapi/v1.jsonAdded explicit Microsoft.AspNetCore.OpenApi 10.0.1 because this project disables central package management; local no-dependency build passed.
Verdict.WebServicesrc/__Libraries/StellaOps.Verdict.WebService/Program.csSource wired; not active matrix/publish target/openapi/v1.json if separately hostedThe library-host source exists and was wired earlier in Sprint 059, but there is no active verdict-web entry in services-matrix.env or build-service-publish.sh; do not count it in live rebuild proof until a service owner reactivates it.
AirGap.Controllersrc/AirGap/StellaOps.AirGap.Controller/Program.csYes/openapi/v1.jsonAdded the central Microsoft.AspNetCore.OpenApi package and default AddOpenApi/MapOpenApi; local no-dependency build passed.
AirGap.Timesrc/AirGap/StellaOps.AirGap.Time/Program.csYes/openapi/v1.jsonAdded the central Microsoft.AspNetCore.OpenApi package and default AddOpenApi/MapOpenApi; local no-dependency build passed.

OpenAPI wiring added 2026-05-20 (Sprint 059 finisher) to the four previously-unwired active hosts so the fan-out reaches them: Graph.Api, Registry.TokenService, ReleaseOrchestrator.WebApi, and Signals now register AddEndpointsApiExplorer()+AddOpenApi() and expose app.MapOpenApi().AllowAnonymous() at /openapi/v1.json (mirroring the policy-engine pattern). Microsoft.AspNetCore.OpenApi was added to the Graph and Signals csproj; registry-token and release-orchestrator already referenced it.

Additional HTTP hosts observed but not changed in this WebService-scoped pass: Policy.Gateway, SmRemote.Service, and Gateway.WebService. Publish-helper alignment on 2026-05-18 added missing active matrix keys for attestor, doctor-web, packsregistry-web, replay-web, symbols, vexlens-web, and airgap-time; it intentionally did not add inactive or merged keys such as timeline-indexer-web, notifier-web, remediation-web, or verdict-web.

Runtime probe note (2026-05-18): the local compose stack was running, but OAPI-059-001’s runtime acceptance criterion is not yet met. Direct host probes were used because the checked container image did not expose curl, wget, or python3; docker exec stellaops-platform ... only found dotnet. Probes returned 200 with OpenAPI 3.1.1 for Platform, 401 for EvidenceLocker/ExportCenter/ReachGraph/BinaryIndex, 400 for Notify, and 404 for the other edited services that are currently deployed in the stack. These results reflect the running containers, not a rebuilt deployment containing this worker’s new package/wiring edits.

Runtime probe note (2026-05-19 follow-up): GET https://stella-ops.local/api/openapi/aggregate still returns 34 services with ok=7, missing=24, and error=3; Scanner remains missing. Direct host-mapped probes confirm valid specs for Platform (http://127.1.0.3/openapi/v1.json, 273 paths), EvidenceLocker (http://127.1.0.7/openapi/v1.json, 35 paths), PacksRegistry (http://127.1.0.34/openapi/v1.json, 16 paths), Authority (http://127.1.0.4/.well-known/openapi, 6 paths), Concelier (http://127.1.0.9/.well-known/openapi, 175 paths), and Excititor (http://127.1.0.10/openapi/excititor.json, 13 paths). Scanner is healthy and built from current HEAD, and /app/Microsoft.AspNetCore.OpenApi.dll is present in the container, but http://127.1.0.8/openapi/v1.json and common alternate paths return 404. Treat Scanner as a route-registration/runtime blocker before claiming merged-spec coverage.

Runtime proof (2026-05-20, Sprint 059 finisher — RESOLVED): after wiring + rebuilding the four unwired hosts (graph, signals, registry-token, release-orchestrator) and recreating their containers, GET https://stella-ops.local/api/openapi/aggregate returns 35 services with ok=33, error=1, missing=1 = 94.3% (up from 82.9%). All four newly-wired hosts serve OpenAPI 3.1.1 at /openapi/v1.json (verified service-to-service). The merged GET /openapi.json (OpenAPI 3.1.0) contains Authority, Platform, EvidenceLocker, and Concelier paths. Residual non-ok: findings-ledger (error, DNS-alias; Findings hard-constraint area) and scanner (missing, route-registration, by-design) — both out-of-scope for this worker; the ≥95% target is reached automatically once either residual is fixed by its owner.

Fan-Out Aggregation (OAPI-059-002)

StellaOps.Gateway.WebService owns the runtime fan-out implementation:

Status values:

StatusMeaning
okA valid OpenAPI document was fetched and parsed.
missingThe service responded but no candidate path returned a valid OpenAPI document.
timeoutThe service did not complete within the per-service timeout.
errorThe fan-out HTTP request failed before a usable response was received.

Regeneration Entrypoint

OAPI-059-004 adds the deterministic documentation regeneration entrypoint:

powershell -ExecutionPolicy Bypass -File .\scripts\docs\regen-from-openapi.ps1 -BaseUrl https://stella-ops.local -Token $env:STELLA_ADMIN_TOKEN

The script reads /api/openapi/aggregate, processes only status: ok service entries, writes each raw spec to docs/modules/<service>/openapi/v1.json, and renders docs/modules/<service>/api-reference.md through the first-party .NET doc generator. Each generated markdown file starts with the OAPI-059 sentinel header and must not be hand-edited. For offline or CI fixture verification, pass -AggregatePath <aggregate.json> and a temporary -OutRoot.

Postman collection generation is intentionally skipped in OAPI-059 because the selected generator is first-party .NET and no offline OpenAPI-to-Postman converter was approved or vendored.

Truthfulness Guardrails

OAPI-059-005 adds .gitea/workflows/openapi-truthfulness.yml and the local wrapper .gitea/scripts/test/run-openapi-truthfulness-check.sh.

Contributor flow for endpoint contract changes: change the endpoint source, rerun tools/scripts/docs/regen-from-openapi.ps1, commit the generated spec and api-reference.md, and add docs/changelog/openapi/<date>-<service>-<summary>.md.

Runtime Flow

Client -> Gateway /api/openapi/aggregate
Gateway -> router state: distinct HELLO-registered services
Gateway -> http://<service>.stella-ops.local/<openapi-path>
Gateway -> cached per-service envelope
Gateway -> merged /openapi.json and /openapi.yaml views

Deprecated Legacy HELLO Flow

┌──────────────┐    HELLO     ┌──────────────┐    GET /openapi.json    ┌──────────────┐
│  Billing     │ ──────────►  │              │  ◄─────────────────────  │   Client     │
│  Service     │   + schemas  │   Gateway    │                          │              │
└──────────────┘              │              │    OpenAPI 3.1.0         │              │
                              │              │  ─────────────────────►  │              │
┌──────────────┐    HELLO     │              │    unified document      └──────────────┘
│  Inventory   │ ──────────►  │              │
│  Service     │   + schemas  └──────────────┘
└──────────────┘

This HELLO-broadcast model is deprecated. It expected services to broadcast schemas and endpoint metadata in the HELLO payload, but Sprint 059 probes showed /openapi.json returning an empty paths object because service schemas were not broadcast in practice. The HELLO payload still identifies connected services; the OpenAPI source of truth is now each service’s own spec endpoint.


Configuration

OpenApiAggregationOptions

Configure OpenAPI aggregation in your Gateway configuration:

# router.yaml or appsettings.yaml
OpenApi:
  Title: "My API Gateway"
  Description: "Unified API for all microservices"
  Version: "2.0.0"
  ServerUrl: "https://api.example.com"
  CacheTtlSeconds: 60
  Enabled: true
  LicenseName: "BUSL-1.1"
  ContactName: "API Team"
  ContactEmail: "api@example.com"
  TokenUrl: "/auth/token"

Configuration Reference

PropertyTypeDefaultDescription
Titlestring"StellaOps Gateway API"API title in OpenAPI info section
Descriptionstring"Unified API aggregating all connected microservices."API description
Versionstring"1.0.0"API version number
ServerUrlstring"/"Base server URL
CacheTtlSecondsint60Cache time-to-live in seconds
EnabledbooltrueEnable/disable OpenAPI aggregation
LicenseNamestring"BUSL-1.1"License name in OpenAPI info
ContactNamestring?nullContact name (optional)
ContactEmailstring?nullContact email (optional)
TokenUrlstring"/auth/token"OAuth2 token endpoint URL

Disabling OpenAPI

To disable OpenAPI aggregation entirely:

OpenApi:
  Enabled: false

Endpoints

Discovery Endpoint

GET /.well-known/openapi

Returns metadata about the OpenAPI document:

Response:

{
  "openapi_json": "/openapi.json",
  "openapi_yaml": "/openapi.yaml",
  "aggregate_json": "/api/openapi/aggregate",
  "etag": "\"5d41402abc4b2a76b9719d911017c592\"",
  "generated_at": "2025-01-15T10:30:00.0000000Z"
}

Per-Service Aggregate

GET /api/openapi/aggregate

Returns one entry per HELLO-registered service:

{
  "generatedAtUtc": "2026-05-18T00:00:00.0000000Z",
  "services": [
    {
      "service": "platform",
      "status": "ok",
      "path": "/openapi/v1.json",
      "fetchedAtUtc": "2026-05-18T00:00:00.0000000Z",
      "durationMs": 12.4,
      "spec": {
        "openapi": "3.1.0",
        "paths": {}
      }
    },
    {
      "service": "scanner",
      "status": "missing",
      "fetchedAtUtc": "2026-05-18T00:00:00.0000000Z",
      "durationMs": 4.1
    }
  ]
}

OpenAPI JSON

GET /openapi.json

Returns the full OpenAPI 3.1.0 specification in JSON format.

Headers:

Conditional Request:

GET /openapi.json
If-None-Match: "5d41402abc4b2a76b9719d911017c592"

Returns 304 Not Modified if content unchanged.

OpenAPI YAML

GET /openapi.yaml

Returns the full OpenAPI 3.1.0 specification in YAML format.

Headers:


Security Mapping

In fan-out mode, operation security metadata comes from each upstream OpenAPI document and is preserved in the merged view. The legacy HELLO-backed generator mapped router RequiringClaims into security schemes as described below; that behavior remains historical reference until the HELLO generator is removed.

Claim to Scope Mapping

When endpoints define RequiringClaims, these are converted to OAuth2 scopes:

// Endpoint with claim requirements
[StellaEndpoint("POST", "/invoices")]
[RequireClaim("billing:write")]
public sealed class CreateInvoiceEndpoint : IStellaEndpoint<...>

Becomes in OpenAPI:

{
  "paths": {
    "/invoices": {
      "post": {
        "security": [
          {
            "BearerAuth": [],
            "OAuth2": ["billing:write"]
          }
        ]
      }
    }
  }
}

Security Schemes

The Gateway generates two security schemes:

BearerAuth

HTTP Bearer token authentication (always present):

{
  "BearerAuth": {
    "type": "http",
    "scheme": "bearer",
    "bearerFormat": "JWT",
    "description": "JWT Bearer token authentication"
  }
}

OAuth2

Client credentials flow with collected scopes (only if endpoints have claims):

{
  "OAuth2": {
    "type": "oauth2",
    "flows": {
      "clientCredentials": {
        "tokenUrl": "/auth/token",
        "scopes": {
          "billing:write": "Access scope: billing:write",
          "billing:read": "Access scope: billing:read",
          "inventory:read": "Access scope: inventory:read"
        }
      }
    }
  }
}

Scope Collection

Scopes are automatically collected from all connected services. If multiple endpoints require the same claim, it appears only once in the scopes list.

Legacy HELLO Compatibility

x-stellaops-gateway-auth.requiresAuthentication is emitted from the Gateway’s effective authorization semantics, not only the raw endpoint flag.


Generated Document Structure

The aggregated OpenAPI document follows this structure:

{
  "openapi": "3.1.0",
  "info": {
    "title": "StellaOps Gateway API",
    "version": "1.0.0",
    "description": "Unified API aggregating all connected microservices.",
    "license": {
      "name": "BUSL-1.1"
    },
    "contact": {
      "name": "API Team",
      "email": "api@example.com"
    }
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "paths": {
    "/invoices": {
      "post": {
        "operationId": "billing_invoices_POST",
        "tags": ["billing"],
        "summary": "Create invoice",
        "description": "Creates a new draft invoice",
        "security": [
          {
            "BearerAuth": [],
            "OAuth2": ["billing:write"]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/billing_CreateInvoiceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/billing_CreateInvoiceResponse"
                }
              }
            }
          },
          "400": { "description": "Bad Request" },
          "401": { "description": "Unauthorized" },
          "404": { "description": "Not Found" },
          "422": { "description": "Validation Error" },
          "500": { "description": "Internal Server Error" }
        }
      }
    },
    "/items": {
      "get": {
        "operationId": "inventory_items_GET",
        "tags": ["inventory"],
        "summary": "List items",
        "responses": {
          "200": { "description": "Success" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "billing_CreateInvoiceRequest": {
        "type": "object",
        "required": ["customerId", "amount"],
        "properties": {
          "customerId": { "type": "string" },
          "amount": { "type": "number" },
          "description": { "type": ["string", "null"] },
          "lineItems": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/billing_LineItem" }
          }
        }
      },
      "billing_CreateInvoiceResponse": {
        "type": "object",
        "required": ["invoiceId", "createdAt", "status"],
        "properties": {
          "invoiceId": { "type": "string" },
          "createdAt": { "type": "string", "format": "date-time" },
          "status": { "type": "string" }
        }
      },
      "billing_LineItem": {
        "type": "object",
        "required": ["description", "amount"],
        "properties": {
          "description": { "type": "string" },
          "amount": { "type": "number" },
          "quantity": { "type": "integer", "default": 1 }
        }
      }
    },
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JWT Bearer token authentication"
      },
      "OAuth2": {
        "type": "oauth2",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "/auth/token",
            "scopes": {
              "billing:write": "Access scope: billing:write"
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "billing",
      "description": "billing microservice (v1.0.0)"
    },
    {
      "name": "inventory",
      "description": "inventory microservice (v2.0.0)"
    }
  ]
}

Schema Prefixing

Schemas are prefixed with the service name to prevent naming conflicts:

ServiceOriginal TypePrefixed Schema ID
billingCreateInvoiceRequestbilling_CreateInvoiceRequest
inventoryGetItemResponseinventory_GetItemResponse

Tag Generation

Tags are automatically generated from connected services:

Operation IDs

Operation IDs follow the pattern: {serviceName}_{path}_{method}

Example: billing_invoices_POST


Cache Behavior

TTL-Based Expiration

The document cache expires based on CacheTtlSeconds (default: 60 seconds):

OpenApi:
  CacheTtlSeconds: 30  # More frequent regeneration

Setting CacheTtlSeconds: 0 regenerates the document on every request (not recommended for production).

Connection-Based Invalidation

The cache is automatically invalidated when:

  1. A new microservice connects (HELLO received)
  2. A microservice disconnects (connection closed)

This ensures the OpenAPI document always reflects currently connected services.

ETag Consistency

The ETag is computed from the document content hash (SHA256). This ensures:

// Store ETag from previous response
let cachedETag = localStorage.getItem('openapi-etag');

const response = await fetch('/openapi.json', {
  headers: cachedETag ? { 'If-None-Match': cachedETag } : {}
});

if (response.status === 304) {
  // Use cached document
  return getCachedDocument();
}

// Store new ETag and document
localStorage.setItem('openapi-etag', response.headers.get('ETag'));
const document = await response.json();
cacheDocument(document);
return document;

Service Registration

Microservice Options

Configure service metadata that appears in OpenAPI:

services.AddStellaMicroservice(options =>
{
    options.ServiceName = "billing";
    options.ServiceDescription = "Invoice and payment processing service";
    options.ContactInfo = "billing-team@example.com";
});

Service OpenAPI Info

The ServiceOpenApiInfo is sent in the HELLO payload:

{
  "instance": { ... },
  "endpoints": [ ... ],
  "schemas": { ... },
  "openApiInfo": {
    "title": "billing",
    "description": "Invoice and payment processing service"
  }
}

This description appears in the tag entry for the service.


Troubleshooting

Document Not Updating

  1. Check CacheTtlSeconds - may need to wait for TTL expiration
  2. Verify service connected successfully (check Gateway logs)
  3. Force refresh by restarting the Gateway

Missing Schemas

  1. Ensure [ValidateSchema] attribute is applied to endpoints
  2. Check for schema parsing errors in Gateway logs
  3. Verify endpoint implements IStellaEndpoint<TRequest, TResponse>

Security Schemes Not Appearing

  1. OAuth2 scheme only appears if endpoints have claim requirements
  2. Check RequiringClaims is populated on endpoint descriptors
  3. Verify claim types are being transmitted correctly

See Also