Concelier Connector Disposition
This ledger records connectors that have been removed, folded into another source, or otherwise retired, together with the rationale and date. It is the authoritative answer to “why is there no <x> connector / source in the tree?” so future agents do not re-add a dead or duplicate source.
For the live connector roster see docs/modules/concelier/connectors.mdand the runtime bundle inventory at src/Concelier/__Libraries/StellaOps.Concelier.Core/RuntimePluginBundleInventory.cs.
| Connector | Status | Reason | Date |
|---|---|---|---|
azure (StellaOps.Concelier.Connector.Cloud.Azure, plugin id cloud-azure, source key azure) | Removed | Standalone connector was an intentional no-op — FetchAsync/ParseAsync/MapAsync all returned Task.CompletedTask and produced 0 advisories (catalog-alias-only, per Sprint 20260505_038). Azure security-advisory coverage is already provided by the MSRC connector (StellaOps.Concelier.Connector.Vndr.Msrc, plugin id vndr-msrc, source key microsoft), which ingests the full Microsoft product family — including Azure — via api.msrc.microsoft.com/sug/v2.0/ with first-party attribution. The standalone azure source was therefore a dead/duplicate source and removed to avoid operator confusion and a fragile parallel scraper target. | 2026-06-25 |
auscert / ACSC (StellaOps.Concelier.Connector.Acsc, plugin id acsc, source key auscert, capability fetch:auscert) | Removed from active plugin set (source retained) | Data-availability disposition, not a code defect — the connector code is sound (32/32 unit tests green). In this deployment there is no viable data path: (1) the lab advisory-fixture.stella-ops.local serves a generic 49-byte health stub at the auscert path, not real ACSC RSS, so ParseAsync has nothing valid to ingest; (2) the real upstream www.cyber.gov.au sits behind Akamai Bot Manager, which terminates DC-ASN HTTP/2 streams and blocks datacenter egress; and (3) no RelayEndpoint is configured to proxy the fetch from a residential/allowed egress. With both the fixture and the live feed dead and no relay, the connector can only fail or no-op, so it is dropped from the recommended runtime bundle set until a relay or real-data path exists. The connector source project and tests are intentionally retained (src/Concelier/__Libraries/StellaOps.Concelier.Connector.Acsc/, src/Concelier/__Tests/StellaOps.Concelier.Connector.Acsc.Tests/) — re-add the packaging entry once a data path is available. | 2026-06-25 |
cert-in (StellaOps.Concelier.Connector.CertIn, plugin id cert-in, source key cert-in) | Removed from active plugin set — not geographically reachable (source retained) | Geographic/network unreachability, not a code defect. cert-in.org.in is unreachable from datacenter egress (TLS handshake fails / HTTP 502; see CertInDependencyInjectionRoutine.cs). Dropped from package-runtime-plugins.ps1 ($recommendedProjects) + RuntimePluginBundleInventory.cs; SourceDefinitions.CertIn is EnabledByDefault=false + tagged geo-unreachable; the live vuln.sources row is kept (enabled=false, metadata.reachability="not-geographically-reachable"). Connector source + tests retained. Re-add to packaging + inventory + flip the markers once a regional relay/real-data path exists. | 2026-06-25 |
auscert / ACSC — also marked not geographically reachable 2026-06-25 | (see below) | In addition to the data-availability removal noted below, SourceDefinitions.AusCert is now EnabledByDefault=false + tagged geo-unreachable, and the live vuln.sources row is kept with metadata.reachability="not-geographically-reachable" — cyber.gov.au (Akamai Bot Manager) is not reachable from DC egress. | 2026-06-25 |
adobe (StellaOps.Concelier.Connector.Vndr.Adobe, plugin id vndr-adobe, source key adobe) | Kept — fixed via compose override | Fixture-date-aging, not a code defect — the connector code is sound (17/17 unit tests green). The connector computes its fetch window from wall-clock time: backfillStart = now - InitialBackfill (default 90 days), and drops index entries with PublishedUtc < windowStart as droppedOldByWindow (AdobeConnector.cs:297-304,379). The committed offline fixture (devops/compose/fixtures/integration-fixtures/advisory/data/vendor/adobe/adobe-index.html) carries APSB25-85 (Sep 9, 2025) and APSB25-87 (Sep 8, 2025); those dates have aged out of the default 90-day window relative to the current wall clock, so a live fixture fetch ingests 0 advisories. The fixture cannot be re-dated because _PROVENANCE.md (sprint 20260504_004 FIXTURE-002) requires it to stay byte-identical to its upstream test fixture, which keeps the date-anchored AdobeConnectorFetchTests deterministic. Fix: a fixture-mode compose override (devops/compose/docker-compose.adobe-fixture.override.yml) sets Concelier__Sources__Adobe__InitialBackfill=36500.00:00:00 (~100 years) so the frozen Sep-2025 entries stay in-window — the same pattern docker-compose.nvd-real.override.yml / docker-compose.osv-real.override.yml use for their frozen feeds. No code, fixture, or image change. | 2026-06-25 |
distro-arch (StellaOps.Concelier.Connector.Distro.Arch, plugin id distro-arch, source key distro-arch) | Kept — fixed (live-fetch path implemented) | Missing capability, not a data-availability or fixture issue — the connector was snapshot-only with NO live-fetch code path. FetchAsync only read ASA JSON files from a local snapshot directory whose default (src/__Tests/__Datasets/os/arch-asa-snapshot) is a test dataset absent from the runtime image, so a stock deployment ingested 0 advisories (the directory simply did not exist → warn + no-op). ArchOptions.BaseUri was declared but never consulted; no HTTP client was registered and nothing ever called the network. Fix: implemented a live-fetch path against the Arch Linux Security Tracker JSON feed (https://security.archlinux.org/json, alias /all.json). ArchTrackerFeedParser parses the feed’s array of AVG (Arch Vulnerability Group) objects, projects each onto the connector’s existing ArchAsaDto, and the connector re-serializes each group into canonical ASA JSON and persists it as a per-advisory source_document (source.kind=live) so the unchanged parse/map pipeline produces pkg:alpm/arch advisories. BaseUri now defaults to the tracker host (live-first); operators null it out to force snapshot-only (air-gap). On live failure the connector falls back to the offline snapshot (FallbackToSnapshotOnLiveFailure=true). Also fixed a latent fetch→parse bug shared by the snapshot path: documents now persist their raw Payload so ParseAsync recovers bytes after a worker scope reset. Going live requires egress to security.archlinux.org (or an offline snapshot provisioned at Concelier__Sources__Arch__SnapshotPath); no image rebuild is needed for code already in the image — re-run source:distro-arch:fetch→:parse→:map. | 2026-06-25 |
distro-gentoo (StellaOps.Concelier.Connector.Distro.Gentoo, plugin id distro-gentoo-glsa, source key distro-gentoo-glsa) | Kept — fixed (live-fetch path implemented) | Missing capability, intentionally deferred — the GLSA connector was an offline snapshot loader with NO live fetcher. FetchAsync only read glsa-*.xml files from a local snapshot directory whose default (src/__Tests/__Datasets/os/gentoo-glsa-snapshot) is a test dataset absent from the runtime image, so a stock deployment ingested 0 advisories (directory missing → warn + no-op). The connector AGENTS.md explicitly stated “does not perform live HTTP”. Fix: implemented a live-fetch path against security.gentoo.org. GentooGlsaIndexParser parses the GLSA plaintext listing index (glsa-index.txt) into canonical GLSA ids (YYYYNN-NN), the connector GETs each referenced glsa-{id}.xml and persists it as a per-advisory source_document (source.kind=live) so the unchanged GlsaXmlParser→Parse→Map pipeline produces pkg:gentoo/<cat>/<name> advisories. BaseUri now defaults to the Gentoo security host (live-first); operators null it out to force snapshot-only (air-gap). On live failure the connector falls back to the offline snapshot (FallbackToSnapshotOnLiveFailure=true). Same latent fetch→parse bug as Arch (Postgres stamps PayloadId = row.Id) fixed in the new live path: documents persist their raw Payload and use the document id as the blob id so ParseAsync recovers bytes after a worker scope reset. Going live requires egress to security.gentoo.org (or an offline snapshot provisioned at Concelier__Sources__Gentoo__SnapshotPath); the new code must ship in a rebuilt image — then re-run source:distro-gentoo-glsa:fetch→:parse→:map. | 2026-06-25 |
Removal details — azure
What was deleted
- Project:
src/Concelier/__Libraries/StellaOps.Concelier.Connector.Cloud.Azure/(entire directory). - Solution:
StellaOps.Connector.Cloud.Azureproject declaration,GlobalSectionbuild-config rows, and nesting entry removed fromsrc/StellaOps.sln. - Shared test project
StellaOps.Concelier.Connector.CloudHardwareAliases.Tests: removed the AzureProjectReferenceand theazurerow from theCloudAliasestheory data. The test project itself is retained — it also covers AWS, GCP, and the Intel/AMD/Siemens hardware connectors. - Packaging:
cloud-azureplugin entry removed fromdevops/build/package-runtime-plugins.ps1. - Runtime inventory:
cloud-azureOptionalConnector(...)row removed fromsrc/Concelier/__Libraries/StellaOps.Concelier.Core/RuntimePluginBundleInventory.cs. - Built bundle artifacts removed from disk (
devops/plugins/concelier/recommended/cloud-azure/,devops/plugins/_publish/concelier/StellaOps.Concelier.Connector.Cloud.Azure/). These are git-ignored build outputs; the authoritative bundle definition lives in the packaging script + inventory above.
What was intentionally NOT touched
- The seeded DB catalog row
('azure', ...)insrc/Concelier/__Libraries/StellaOps.Concelier.Persistence/Migrations/001_v1_concelier_baseline.sqlis left in place. It isenabled=false, so it ingests nothing and is harmless. Migration policy is forward-only (ADR-004); editing the baseline insert would not affect already-migrated databases anyway. If an operator wants the orphaned row gone, a future forward migration canDELETE FROM vuln.sources WHERE key = 'azure'. - Historical packaging run reports under
devops/plugins/_reports/*.json(git-ignored, timestamped) still mentioncloud-azure; these are immutable build logs and are left as-is.
Coverage proof
Azure advisories continue to flow through MSRC (source 'microsoft'). No advisory coverage is lost by this removal because the azure connector never produced any advisories in the first place.
Verification (2026-06-25)
dotnet build src/Concelier/StellaOps.Concelier.WebService/...csproj— Build succeeded, 0 errors (host no longer references the Azure project).dotnet buildofStellaOps.Concelier.Connector.CloudHardwareAliases.Tests— 0 errors / 0 warnings.- MTP exe run of that test suite — 13 tests, 0 errors; the single failure is the pre-existing
Alias_Count_Matches_Seed_Migrationshardware-count assertion (asserts 4 hardware aliases,HardwareAliaseslists only 3 — ARM not wired), which was already red at HEAD before this change and is tracked separately. The cloud-count portion of that test (Assert.Equal(2, CloudAliases.Count())) passes, and all AWS/GCP/hardware behavioral cases pass.
Removal details — auscert / ACSC
Why removed (data-availability, not a code defect)
The ACSC connector (source key 'auscert') is removed from the active deployed plugin set because this deployment has no working data path into it:
- Lab fixture is a stub, not data. The fixture-mode override points the connector at
http://advisory-fixture.stella-ops.local/cert/auscert/(devops/compose/docker-compose.fixture-mode.yml), but in this deployment that path returns a generic 49-byte health stub, not real ACSC RSS.ParseAsynctherefore has no valid feed to ingest. - Real upstream is bot-blocked from DC egress.
www.cyber.gov.ausits behind Akamai Bot Manager, which frequently terminates datacenter-ASN HTTP/2 streams. The realfeeds override already documents this as best-effort /SCRAPER / REGION_CERT(devops/compose/docker-compose.realfeeds-full.override.yml, theConcelier__Sources__Auscert__BaseEndpoint=https://www.cyber.gov.au/line). - No relay configured. There is no
RelayEndpointwired to proxy the fetch from a residential / allowed egress, so neither (1) nor (2) yields data.
With the fixture dead and the live feed unreachable and no relay, the connector can only fail or no-op. Keeping it mounted produces failing/empty jobs and operator confusion, so it is dropped from the recommended runtime bundle set until a viable data path exists.
What was changed
- Packaging:
acscplugin entry removed from the$recommendedProjectslist indevops/build/package-runtime-plugins.ps1(replaced with a comment explaining why and how to re-add it). - Runtime inventory:
acscOptionalConnector(...)row removed fromsrc/Concelier/__Libraries/StellaOps.Concelier.Core/RuntimePluginBundleInventory.cs(replaced with an explanatory comment). - Built bundle artifacts removed from disk (
devops/plugins/concelier/recommended/acsc/,devops/plugins/_publish/concelier/StellaOps.Concelier.Connector.Acsc/). These are git-ignored build outputs; the authoritative bundle definition lives in the packaging script + inventory above. Removing the directory from the recommended profile means it will not be bind-mounted into the live Concelier service on the next deploy (the service mounts../plugins/concelier/recommended:/app/plugins/concelier/recommended:ro).
What was intentionally NOT touched
- The connector source project is retained in full (
src/Concelier/__Libraries/StellaOps.Concelier.Connector.Acsc/). This is a data-availability disposition, not a code-quality removal — the connector parses ACSC RSS correctly. - The connector test project is retained (
src/Concelier/__Tests/StellaOps.Concelier.Connector.Acsc.Tests/) and stays green (32/32). - Compose fixtures and the
Concelier__Sources__Auscert__*env wiring in the fixture-mode / realfeeds overrides are left as-is — they document the (currently non-viable) feed shape and are the re-enable point once a relay exists. - Any seeded DB catalog row for
auscertand historical git-ignored packaging run reports underdevops/plugins/_reports/*.jsonare left as-is (forward-only migration policy; immutable build logs).
Re-enable path
When a relay or real-data path becomes available, re-add the acsc entry to both package-runtime-plugins.ps1 ($recommendedProjects) and RuntimePluginBundleInventory.cs (OptionalConnector("acsc", "auscert", ...)), re-run package-runtime-plugins.ps1 to regenerate the devops/plugins/concelier/recommended/acsc/ bundle, point Concelier__Sources__Auscert__BaseEndpoint (and a RelayEndpoint) at the working source, then redeploy Concelier and re-run source:auscert:fetch.
Verification (2026-06-25)
dotnet build src/Concelier/__Libraries/StellaOps.Concelier.Core/...csproj— Build succeeded, 0 errors (inventory edit compiles; 2 pre-existing STELLA0100 analyzer warnings unrelated to this change).dotnet build src/Concelier/StellaOps.Concelier.WebService/...csproj— Build succeeded, 0 errors (Concelier host still compiles after the removal).dotnet buildofStellaOps.Concelier.Connector.Acsc.Tests— 0 errors / 0 warnings (retained connector + tests still compile).- MTP exe run of
StellaOps.Concelier.Connector.Acsc.Tests.exe— Total: 32, Errors: 0, Failed: 0, Skipped: 0 — confirming the connector code is sound and the removal is purely a data-availability decision.
Fix details — adobe
Root cause (fixture-date-aging, not a code defect)
AdobeConnector.LoadIndexAsync derives its fetch window from wall-clock time (src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Adobe/AdobeConnector.cs:297-304):
var now = _timeProvider.GetUtcNow(); // real clock in prod
var backfillStart = now - _options.InitialBackfill; // InitialBackfill default = 90 days
var windowStart = cursor.LastPublished.HasValue
? cursor.LastPublished.Value - _options.WindowOverlap
: backfillStart;
if (windowStart < backfillStart) windowStart = backfillStart;
and then drops any index entry older than the window as droppedOldByWindow (AdobeConnector.cs:379: if (entry.PublishedUtc < windowStart) ...).
The committed offline fixture (devops/compose/fixtures/integration-fixtures/advisory/data/vendor/adobe/adobe-index.html) lists two bulletins published September 8–9, 2025:
- APSB25-85 — Adobe Acrobat Reader — “September 9, 2025”
- APSB25-87 — Adobe Premiere Pro — “09/08/2025”
With the default 90-day InitialBackfill, backfillStart is ~90 days before the current wall clock (now far past Sep 2025), so both fixture entries fall before windowStart and are dropped. The connector therefore ingests 0 advisories from the fixture — even though the code path is correct.
Why the fixture is not the fix
devops/compose/fixtures/.../adobe/_PROVENANCE.md (frozen at sprint 20260504_004, criterion FIXTURE-002) requires the fixture to remain byte-identical to its upstream test-fixture source so the connector’s date-anchored AdobeConnectorFetchTests (which pin a FakeTimeProvider to 2025-09-10) stay deterministic. Re-dating the fixture HTML would break that contract. The fix instead widens the runtime window so the frozen dates stay in-range.
What was changed
- New override file
devops/compose/docker-compose.adobe-fixture.override.ymlsetsConcelier__Sources__Adobe__InitialBackfill=36500.00:00:00(~100 years). This is the same approach asdocker-compose.nvd-real.override.yml(Concelier__Sources__Nvd__InitialBackfill) anddocker-compose.osv-real.override.yml(Concelier__Sources__Osv__InitialBackfill=36500.00:00:00), both of which setInitialBackfillfor the identical frozen-date reason. - The env-var key maps to the canonical config section
concelier:sources:adobe, whichAdobeDependencyInjectionRoutine.Registerbinds last (highest precedence,AdobeDependencyInjectionRoutine.cs:35).
What was intentionally NOT touched
- The connector source and tests (
src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Adobe/,src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests/) — the code is correct. - The byte-identical fixture under
devops/compose/fixtures/integration-fixtures/advisory/data/vendor/adobe/and the testFixtures/corpus — preserves_PROVENANCE.md/ FIXTURE-002 determinism. - The Adobe wiring in
docker-compose.fixture-mode.yml/docker-compose.realfeeds-full.override.yml— left as-is.
Deploy path (post-drains, coordinated — NOT done in this change)
Append docker-compose.adobe-fixture.override.yml to the compose chain after the base files so it wins, recreate/redeploy Concelier with the existing fixture-mode chain, then re-run source:vndr-adobe:fetch → :parse → :map. No image rebuild and no egress are required — the data already lives in the in-repo fixture.
Verification (2026-06-25)
dotnet buildofStellaOps.Concelier.Connector.Vndr.Adobe.csproj— Build succeeded, 0 errors (2 pre-existing STELLA0100 canonicalization warnings in unrelatedConcelier.Core/StellaOps.Pluginfiles).dotnet buildofStellaOps.Concelier.Connector.Vndr.Adobe.Tests.csproj— 0 errors / 0 warnings.- MTP exe run of
StellaOps.Concelier.Connector.Vndr.Adobe.Tests.exe— Total: 17, Errors: 0, Failed: 0, Skipped: 0 (Postgres testcontainer) — confirming the connector code is sound and the issue is purely fixture-date aging, addressed by the runtime override.
Fix details — distro-arch
Root cause (missing capability — offline-snapshot-only, no live-fetch)
The Arch connector shipped with FetchAsync reading ASA JSON files from a local snapshot directory only (src/Concelier/__Libraries/StellaOps.Concelier.Connector.Distro.Arch/ArchConnector.cs, old FetchAsync → ArchSnapshotReader.Read). Its default snapshot path (ArchOptions.DefaultSnapshotRelativePath = "src/__Tests/__Datasets/os/arch-asa-snapshot") is a test dataset that is not present in the runtime image, so on a stock deployment the directory does not exist → the connector logs a warning and no-ops, ingesting 0 advisories. ArchOptions.BaseUri existed in the options class but was never read by any code path, no IHttpClientFactory client was registered for the source, and nothing performed a network fetch. The connector was therefore inert in production unless an operator manually provisioned a snapshot directory.
What was changed (live-fetch path against the Arch Security Tracker)
ArchOptions—BaseUrinow defaults tohttps://security.archlinux.org/(live-first); addedLiveFeedPath(defaultjson),FallbackToSnapshotOnLiveFailure(defaulttrue), andResolveLiveFeedUri(). Operators force snapshot-only / air-gap mode by settingConcelier__Sources__Arch__BaseUrito empty.Internal/ArchTrackerFeedParser.cs(new) — parses the live feed, which is a JSON array of AVG (Arch Vulnerability Group) objects (a different shape from the per-advisory ASA object the snapshot pipeline reads), and projects each group onto the existingArchAsaDto. A group’s canonical name is its first published ASA id (falling back to the AVG id) so live and snapshot ingestion key advisories identically; groups with no affected packages or no CVE ids are dropped.ArchConnector.FetchAsync— split intoFetchLiveAsync(HTTP GET of the resolved feed URI via theconcelier.archnamed client, fan-out into per-group documents taggedsource.kind=live) and the prior snapshot path (FetchSnapshotAsync, now taggedsource.kind=snapshot). Live is attempted first whenBaseUri+ anIHttpClientFactoryare present; on failure it falls back to the snapshot (unlessFallbackToSnapshotOnLiveFailure=false, which surfaces the failure and marks the source failed). Each group is re-serialized into canonical ASA JSON via the newSerializeAsaDtoso the unchangedParseAsync/MapAsyncpipeline (andArchAsaParser/ArchMapper) produces the samepkg:alpm/archadvisories.ArchServiceCollectionExtensions— registers theconcelier.archsource HTTP client (AddSourceHttpClient) with the tracker host allowlisted; the connector treats the factory as optional, so this registration is what lights up the live path.- Latent fetch→parse bug fixed in both paths — documents are now persisted with their raw
Payloadand the raw-storage blob id is the document id, soParseAsyncrecovers the bytes via the document store even after a worker scope reset (the Postgres document store stampsPayloadId = row.Idon read). Without this, the previously-untested fetch→parse hop failed withBlob ... not foundon a real Postgres deployment.
What was intentionally NOT touched
- The offline snapshot dataset (
src/__Tests/__Datasets/os/arch-asa-snapshot/) and the existing parser/mapper/snapshot-reader/DTO — they remain the air-gap data path and are unchanged in behaviour. - Job schedules / DI routine cron wiring (
ArchDependencyInjectionRoutine).
Tests (new, in StellaOps.Concelier.Connector.Distro.Arch.Tests)
Fixtures/arch-tracker-feed.json— a representative in-repo fixture reflecting the documented tracker feed shape (AVG groups withpackages/status/severity/affected/fixed/issues/advisories). No external egress was performed authoring it (AGENTS.md §2.8).ArchTrackerFeedParserTests— projection, serialize↔parse round-trip, parse→map producingpkg:alpm/archadvisories, and a malformed-payload guard (no DB).ArchConnectorLiveFetchTests— the live-fetch forcing function: drivesFetchAsyncagainst a canned HTTP response serving the fixture, asserts the GET hit the resolved feed URI, per-groupsource.kind=livedocuments persisted, and the full parse+map landed canonical advisories (bash5.2.026-1fixed, curl unfixed→affected). A second case proves live-failure → snapshot fallback. Uses the shared Postgres testcontainer; skips when Docker is unavailable.
Deploy path (post-drains, coordinated — NOT done in this change)
To make this live, the Concelier deployment needs egress to security.archlinux.org(the code already in a rebuilt image, or this change shipped in the next image build), then re-run source:distro-arch:fetch → :parse → :map. For an air-gapped deployment, instead provision an ASA snapshot directory and point Concelier__Sources__Arch__SnapshotPath at it (and optionally clear Concelier__Sources__Arch__BaseUri to skip the live attempt). No compose override is required for the default live behaviour.
Verification (2026-06-25)
dotnet buildofStellaOps.Concelier.Connector.Distro.Arch.csproj— Build succeeded, 0 errors (2 pre-existing STELLA0100 canonicalization warnings in unrelatedConcelier.Core/StellaOps.Pluginfiles;TreatWarningsAsErrorsis on for the connector project and it is clean).dotnet buildof the test project — 0 errors / 0 warnings.- MTP exe run of
StellaOps.Concelier.Connector.Distro.Arch.Tests.exe— Total: 23, Errors: 0, Failed: 0, Skipped: 0 (Postgres testcontainer), including the new parser tests and the live-fetch forcing-function tests.
Fix details — distro-gentoo
Root cause (missing capability — offline snapshot loader, no live fetcher)
The Gentoo GLSA connector (src/Concelier/__Libraries/StellaOps.Concelier.Connector.Distro.Gentoo/) shipped with FetchAsync reading glsa-*.xml files from a local snapshot directory only. The previous GentooGlsaConnector facade and the connector AGENTS.md both stated the live IFeedConnector HTTP path was intentionally deferred (“does not use SourceFetchService and does not perform live HTTP”). The default snapshot path (GentooGlsaOptions.DefaultSnapshotRelativePath = "src/__Tests/__Datasets/os/gentoo-glsa-snapshot") is a test dataset that is not present in the runtime image, so on a stock deployment the directory does not exist → the connector logs a warning and no-ops, ingesting 0 advisories. No IHttpClientFactory client was registered for the source and nothing performed a network fetch.
What was changed (live-fetch path against security.gentoo.org)
GentooGlsaOptions—BaseUrinow defaults tohttps://security.gentoo.org/glsa/(live-first); addedIndexPath(defaultglsa-index.txt),AdvisoryPathFormat(defaultglsa-{0}.xml),FallbackToSnapshotOnLiveFailure(defaulttrue),FetchTimeout,UserAgent,HttpClientName(concelier.gentoo), andResolveIndexUri()/ResolveAdvisoryUri(id). Operators force snapshot-only / air-gap mode by settingConcelier__Sources__Gentoo__BaseUrito empty.Internal/GlsaIndexParser.cs(new) — parses the GLSA plaintext listing index into canonical GLSA ids (YYYYNN-NN). Permissive about line shape (glsa-202401-01.xml, bare202401-01,#comments and blank lines ignored); output is ordinal-sorted and de-duplicated so the fan-out fetch order is deterministic and independent of upstream ordering.GentooGlsaFeedConnector.FetchAsync— split intoFetchLiveAsync(HTTP GET of the resolved index URI via theconcelier.gentoonamed client, then a GET of each referencedglsa-{id}.xml, persisting per-advisory documents taggedsource.kind=live) and the prior snapshot path (FetchSnapshotAsync, taggedsource.kind=snapshot). Live is attempted first whenBaseUri+ anIHttpClientFactoryare present; on failure it falls back to the snapshot (unlessFallbackToSnapshotOnLiveFailure=false, which surfaces the failure and marks the source failed viaMarkFailureAsync). Parse and Map are unchanged — both live and snapshot documents are GLSA XML consumed by the existingGlsaXmlParser, producing the samepkg:gentoo/<cat>/<name>advisories.GentooGlsaServiceCollectionExtensions— registers theconcelier.gentoosource HTTP client (AddSourceHttpClient) with the Gentoo host allowlisted; the connector treats the factory as optional, so this registration is what lights up the live path.GentooGlsaFeedConnectorgained an optionalIHttpClientFactory?constructor parameter (mirrorsDistro.Arch).GentooGlsaRuntimeOverlay/GentooGlsaConfigurationContributor— surfacebaseUri,indexPath,advisoryPathFormat, andfallbackToSnapshotOnLiveFailureas persisted/overlayable source settings.- Latent fetch→parse bug fixed in the new live path — documents are persisted with their raw
Payloadand the raw-storage blob id is the document id, soParseAsyncrecovers the bytes via the document store even after a worker scope reset (the Postgres document store stampsPayloadId = row.Idon read). The first test run reproduced this exactly asBlob ... not foundon a real Postgres deployment; the fix is identical to thedistro-archfix.
What was intentionally NOT touched
- The offline snapshot data path (
GlsaSnapshotLoader,GentooGlsaConnectorstatic facade) and the existing parser/mapper/DTO — they remain the air-gap data path and are unchanged in behaviour. The snapshotFetchSnapshotAsyncbody is the priorFetchAsyncbody verbatim (still uses the 5-argUploadAsyncwithout persistingPayload); the latent fetch→parse hop on the snapshot path was left as-is to stay strictly scoped to the live-fetch task. - Job schedules / DI routine cron wiring (
GentooGlsaDependencyInjectionRoutine). - Other connectors and the running Concelier service (a backfill drain was in progress; no redeploy/restart/image rebuild was performed).
Tests (new, in StellaOps.Concelier.Connector.Distro.Gentoo.Tests)
Fixtures/live/glsa-index.txt— a representative in-repo GLSA listing index reflecting the documented feed shape (oneglsa-*.xmlper line,#comments). No external egress was performed authoring it (AGENTS.md §2.8); the per-advisory documents reuse the existingFixtures/glsa-*.xmlcorpus.GlsaIndexParserTests— pure unit (no DB), always runs: canonical-id extraction, comment/blank-line skipping, bare-id lines, dedup + ordinal sort, non-GLSA line rejection, empty input.GentooGlsaLiveFetchTests— the live-fetch forcing function: drivesFetchAsyncagainst canned HTTP responses serving the index + glsa XML fixtures, asserts the GET hit the resolved index and per-advisory URIs, per-advisorysource.kind=livedocuments persisted (withgentoo.glsa.id/source.indexUrimetadata), and the full parse+map landed canonicalpkg:gentoo/app-shells/bashandpkg:gentoo/sys-libs/glibcadvisories. A second case proves live-failure → snapshot fallback; a third provesBaseUri=nullstays snapshot-only with zero HTTP requests issued. Uses the shared Postgres testcontainer; skips when Docker is unavailable (the index-parser proof still runs).
Deploy path (post-drains, coordinated — NOT done in this change)
To make this live, the Concelier deployment needs the new code in a rebuilt image (this is a source change, not just a config flip) and egress to security.gentoo.org, then re-run source:distro-gentoo-glsa:fetch → :parse → :map. For an air-gapped deployment, instead provision a GLSA snapshot directory and point Concelier__Sources__Gentoo__SnapshotPath at it (and optionally clear Concelier__Sources__Gentoo__BaseUri to skip the live attempt). No compose override is required for the default live behaviour. Snapshot provisioning alternative: mirror the data/glsa.git glsa-*.xml set (and a glsa-index.txt listing) into an offline-kit directory mounted at the configured SnapshotPath — the connector ingests it identically to the live path.
Verification (2026-06-25)
dotnet buildofStellaOps.Concelier.Connector.Distro.Gentoo.csproj— Build succeeded, 0 errors (2 pre-existing STELLA0100 canonicalization warnings in unrelatedConcelier.Core/StellaOps.Pluginfiles;TreatWarningsAsErrorsis on for the connector project and it is clean).dotnet buildof the test project — 0 errors / 0 warnings.- MTP exe run of
StellaOps.Concelier.Connector.Distro.Gentoo.Tests.exe— Total: 31, Errors: 0, Failed: 0, Skipped: 0 (Postgres testcontainer), including the 6 newGlsaIndexParserTestsand the 3 newGentooGlsaLiveFetchTestslive-fetch forcing-function cases. The first run surfaced (then the fix resolved) the realBlob ... not foundfetch→parse bug.
