Class BowireMetadataKeys

Namespace
Kuestenlogik.Bowire
Assembly
Kuestenlogik.Bowire.dll

Metadata keys that mean something to a protocol plugin instead of being forwarded to the server.

public static class BowireMetadataKeys
Inheritance
BowireMetadataKeys
Inherited Members

Remarks

Per-call metadata is normally headers, but a few entries are configuration for the plugin itself and are stripped before anything reaches the wire. They live here because the two ends of that agreement cannot see each other: a plugin is loaded at runtime and referenced by nobody, so the host and the security scanner had no way to name a key except by repeating the literal — which is what BowireDiscoveryEndpoints was already doing with the transport marker.

The double underscores are not decoration. They mark a key as belonging to Bowire rather than to the caller, so a header genuinely named descriptors cannot collide with one of these.

Fields

GrpcDescriptorSet

A compiled gRPC descriptor set, for a server that does not answer Server Reflection (#653).

public const string GrpcDescriptorSet = "__bowireGrpcDescriptors__"

Field Value

string

Remarks

The value is either a path to a .protoset file or JSON with a path or base64 property. Produced by protoc --descriptor_set_out=api.protoset --include_imports.

GrpcTransport

gRPC transport selection (web for gRPC-Web).

public const string GrpcTransport = "__bowireGrpcTransport"

Field Value

string

Remarks

Also accepted as a query parameter on the server URL, because discovery had no metadata bag when that path was written.

PluginHint

The plugin the caller explicitly pinned with hint@url, if any.

public const string PluginHint = "__bowirePluginHint"

Field Value

string

Remarks

DiscoverAsync has no "was I explicitly asked for?" parameter, and some plugins need one. A plugin that discovers from a bundled schema rather than from the wire cannot probe: it would answer for every URL it is offered, so on the hint-less fan-out it invents services that are not there. The same is true of an ad-hoc separate-target fallback, which must not fire just because some unrelated endpoint happens to answer the right content type.

The hint itself cannot carry the answer: Parse(string?) consumes the hint@ prefix before the plugin is reached, so a plugin gating on that prefix is gating on something it can never see. The TacticalAPI plugin did exactly that and returned an empty list on every path, including the one its own sample depends on, while its unit test passed by calling DiscoverAsync with a prefix production never delivers.

So the core stitches the resolved plugin id onto the URL as this marker whenever a hint was given. One marker for every plugin, rather than the per-plugin ones SSE and SignalR each grew: those had to be kept aligned by hand across two files, and the next plugin to need the same bit either invents a third or, as here, gets it wrong.