zerodds-grpc-bridge v1.0 — Spec Coverage
Source: docs/specs/zerodds-grpc-bridge-1.0.md
Implementation:
crates/grpc-bridge/· docs.rs — DDS↔︎gRPC bridge.
§1 Conformance levels
§1 L1-L6 conformance matrix
Spec: §1 — six levels (Wire/DDS/Bridging/Config/Auth/Multi-Tenant); L1–L4 mandatory, L5–L6 optional.
Repo: —
Tests: —
Status: n/a (informative) — assignment of the levels; the requirements are the items §2 to §12.
§2 CLI surface
§2 zerodds-grpc-bridged CLI
Spec: §2 — options
--config/--bind/--domain/--tls-*/--reflection/--topic/--log-level/--metrics/--version/--help;
exit codes 0/1/2/3/4.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic,
::http2_unknown_service_yields_status_5.
Status: done
§3 Config-file format
§3 YAML loader with ENV substitution
Spec: §3 — top-level domain/grpc/auth/topics/metrics; ENV
substitution.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs (config
parser), crates/grpc-bridge/src/server.rs.
Tests: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::tests: config_default_has_default_bind,
parse_minimal_grpc_config, parse_topics_array, parse_invalid_yaml_returns_err,
example_yaml_parses_without_error, example_yaml_key_fields_match_intent,
example_yaml_topics_reach_the_daemon, example_yaml_has_no_unknown_top_level_keys,
unknown_top_level_key_does_not_abort_the_parse;
crates/config/tests/layering.rs::a_hash_inside_quotes_is_part_of_the_value; ENV substitution is covered by
crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::tests::env_substitution_applies_before_the_parse.
Status: done
§4 gRPC wire protocol
§4.1 HTTP/2 setup with ALPN h2
Spec: §4.1 — an HTTP/2 server, ALPN h2, cleartext h2c only in dev
mode (bind=127.0.0.1); SETTINGS frame, GOAWAY on shutdown.
Repo: crates/grpc-bridge/src/server.rs,
crates/grpc-bridge/src/frame.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§4.2 Service-definition auto-generation per topic
Spec: §4.2 — a <TopicSlug>Stream service with
Publish/Subscribe/PublishOne/Catalog RPCs;
Sample/PublishAck/SubscribeReq/CatalogReq/CatalogResp/TopicEntry
messages.
Repo: crates/grpc-bridge/src/path.rs (slug→service-name),
crates/grpc-bridge/src/service_gen.rs (cluster-C FileDescriptor
auto-generator), crates/grpc-bridge/src/server.rs.
Tests: crates/grpc-bridge/src/service_gen.rs::tests: pascal_simple,
pascal_dotted, pascal_mixed_separators, pascal_empty_falls_back,
topic_service_paths, matches_path_publish, render_proto_contains_methods,
catalog_registration_dedup, catalog_find_by_path,
catalog_fully_qualified_names;
crates/grpc-bridge/src/path.rs::tests::parses_standard_grpc_path,
crates/grpc-bridge/src/path.rs::tests::parses_dotted_service_name;
crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§4.3 RPC flows publish/subscribe
Spec: §4.3 — HEADERS/DATA/HEADERS-END_STREAM flow with :method=POST,
:path=/<pkg>.<svc>/<method>,
content-type/grpc-encoding/grpc-timeout/authorization; streaming per
sample.
Repo: crates/grpc-bridge/src/server.rs,
crates/grpc-bridge/src/metadata.rs, crates/grpc-bridge/src/timeout.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§4.4 Length-prefix wrapper
Spec: §4.4 — a 5-byte header (compr 1B + length 4B BE) + protobuf
sample payload; compr 0=identity / 1=compressed (gzip/deflate via
grpc-encoding).
Repo: crates/grpc-bridge/src/frame.rs.
Tests: inline #[cfg(test)] mod tests in crates/grpc-bridge/src/frame.rs;
crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§4.5 Status mapping OK/INVALID_ARGUMENT/PERMISSION_DENIED/…
Spec: §4.5 — gRPC status codes 0/3/4/7/8/13/14/16 → DDS conditions.
Repo: crates/grpc-bridge/src/status.rs.
Tests: inline #[cfg(test)] mod tests in crates/grpc-bridge/src/status.rs;
crates/grpc-bridge/tests/bridge_e2e.rs::http2_unknown_service_yields_status_5.
Status: done
§4.6 Reflection service grpc.reflection.v1alpha
Spec: §4.6 — the standard reflection service with auto-generated topic services + sample message types.
Repo: crates/grpc-bridge/src/server.rs (reflection hook),
crates/grpc-bridge/src/reflection.rs (cluster-C reflection RPC fully
wired).
Tests: crates/grpc-bridge/src/reflection.rs::tests:
empty_catalog_yields_empty_list_services_response, one_service_in_list,
error_response_encodes_code_and_message, varint_basics,
reflection_path_constant_matches_grpcurl_default;
crates/grpc-bridge/tests/cross_vendor.rs::list_services_reflection_payload_is_well_formed plus the two grpcurl
runs crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_list_services_against_zerodds_daemon and
crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_lists_the_reflection_service_itself (#[ignore], gated on
GRPC_GRPCURL=1).
Status: done
§5 Topic mapping
§5.1 Service-name default Topic→ChatMessageStream
Spec: §5.1 — :: split + CamelCase + suffix Stream; package
zerodds.<segment>.v1.
Repo: crates/grpc-bridge/src/path.rs.
Tests: crates/grpc-bridge/src/service_gen.rs::tests::pascal_simple,
crates/grpc-bridge/src/service_gen.rs::tests::pascal_dotted,
crates/grpc-bridge/src/service_gen.rs::tests::pascal_mixed_separators,
crates/grpc-bridge/src/service_gen.rs::tests::pascal_empty_falls_back,
crates/grpc-bridge/src/service_gen.rs::tests::topic_service_paths;
crates/grpc-bridge/src/path.rs::tests (7 tests) covers the path form
itself.
Status: done
§5.2 Type discovery reflection + catalog + IDL endpoint
Spec: §5.2 — three paths (reflection service, catalog RPC, HTTP-GET sidekick).
Repo: crates/grpc-bridge/src/server.rs,
crates/grpc-bridge/src/reflection.rs,
crates/grpc-bridge/src/service_gen.rs (cluster-C type discovery via
reflection + catalog RPC + HTTP-GET sidekick).
Tests: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::tests::build_catalog_emits_topics,
crates/grpc-bridge/src/daemon_runtime.rs::tests::catalog_render_json_is_valid_shape;
crates/grpc-bridge/src/service_gen.rs::tests::catalog_registration_dedup,
::catalog_find_by_path, ::catalog_fully_qualified_names;
crates/grpc-bridge/src/reflection.rs::tests::one_service_in_list.
Status: done
§6 QoS translation
§6 DDS-QoS → gRPC-behavior map
Spec: §6 — Reliability/Durability/History/Lifespan/Deadline/Liveliness/Partition
map; partition_filter as a subscribe field.
Repo: crates/grpc-bridge/src/server.rs,
crates/grpc-bridge/src/timeout.rs, crates/grpc-bridge/src/metadata.rs,
crates/grpc-bridge/src/qos_translation.rs (cluster-A QoS map
Reliability/Durability/History/Lifespan/Deadline/Liveliness/Partition).
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic
(reliable round-trip); a QoS matrix in
crates/grpc-bridge/src/qos_translation.rs::tests.
Status: done
§7 Security
§7.1 TLS h2-ALPN + cert rotation
Spec: §7.1 — TLS 1.2+ (1.3 default), ALPN h2, SIGHUP cert rotation.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::serve_tls,
::security_from, ::apply_reload;
crates/grpc-bridge/src/daemon_runtime.rs::build_grpc_tls_config_with
(grpc.tls.client_ca_file, grpc.tls.require_client_cert).
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::mtls_serves_h2_and_the_certificate_is_the_subject
(ALPN h2 read at the client),
::a_sighup_serves_the_next_connection_with_the_new_certificate;
against grpc-go crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_over_tls_with_a_bearer_token
(grpcurl 1.9.3, Linux test host);
crates/grpc-bridge/tests/security_e2e.rs::build_grpc_tls_config_sets_alpn_h2,
::build_grpc_tls_config_with_client_ca_for_mtls, ::build_grpc_tls_config_rejects_missing_cert.
Status: done
§7.2 Auth modes none/jwt/mtls
Spec: §7.2 — JWT-bearer authorization metadata, mTLS cert DN; §3
auth.jwt.public_key, audience, required_claim.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::handle_connection
(checked per call on the authorization metadata, UNAUTHENTICATED),
::security_from; crates/grpc-bridge/src/server.rs (GrpcRequest::metadata);
crates/bridge-security/src/auth.rs (RS256, exp/nbf with 60 s leeway,
aud, required claim; jwt_public_key_from_pem for PKCS#1 and SPKI).
Also bearer with auth.bearer_token/auth.bearer_subject. The mTLS
subject is the certificate’s subject DN (RFC 4514, CN=alice); an ACL
rule may name the fingerprint mtls:<SHA-256> instead.
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::bearer_and_acl_are_checked_per_call,
::jwt_checks_signature_time_audience_and_scope,
::mtls_serves_h2_and_the_certificate_is_the_subject,
::jwt_without_a_key_and_mtls_without_tls_do_not_start;
crates/bridge-security/src/auth.rs::tests::a_signed_jwt_is_checked_for_time_audience_and_claim.
Status: done
§7.3 Per-topic ACL
Spec: §7.3 — acl.publish/acl.subscribe lists with subject
resolution.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::dispatch
(Publish = write, Subscribe = read on the DDS topic,
PERMISSION_DENIED); topics[].acl in DaemonConfig::from_config.
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::bearer_and_acl_are_checked_per_call,
::jwt_checks_signature_time_audience_and_scope (JWT sub as the subject),
::mtls_serves_h2_and_the_certificate_is_the_subject (certificate as the subject).
Status: done
§8 Operations + observability
§8.1 Structured JSON logging
Spec: §8.1 — JSON log + a --log-level switch.
Repo: —
Tests: —
Status: open — nothing tests the JSON log form or the --log-level switch.
### §8.2 Prometheus metrics
Spec: §8.2 — --metrics CLI + 10 counter/gauge families.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::SpecMetrics
(all ten zerodds_grpc_bridge_* families). Label values come only from the
configuration or a fixed set: a service that is not mapped counts as
service="unknown", a foreign method as method="other".
bytes_in/bytes_out count the Length-Prefixed-Message including its
5-octet prefix; h2_resets_total counts received RST_STREAM frames by their
RFC 7540 §7 names (the daemon sends none). The older zerodds_grpc_* from
crates/grpc-bridge/src/daemon_runtime.rs stay alongside.
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::the_metrics_of_section_8_2_count_calls_failures_and_resets
(status per call, auth failures by reason, RST_STREAM(CANCEL), with
dds-runtime Publish → DDS → Subscribe);
crates/grpc-bridge/src/daemon_runtime.rs::tests::admin_endpoint_serves_metrics_path.
Status: done
§8.3 OTLP spans + W3C traceparent
Spec: §8.3 — OTEL_EXPORTER_OTLP_ENDPOINT + trace propagation via a
traceparent header.
Repo: crates/grpc-bridge/src/metadata.rs (traceparent parsing hook),
crates/grpc-bridge/src/daemon_runtime.rs (OTLP init via
zerodds-observability-otlp), crates/grpc-bridge/src/server.rs (span emit
per RPC).
Tests: crates/grpc-bridge/src/daemon_runtime.rs::tests::otlp_config_parses_endpoint — the endpoint from
OTEL_EXPORTER_OTLP_ENDPOINT is read. Nothing tests the traceparent
parsing — see §8.3a.
Status: partial — the OTLP configuration is covered, the W3C traceparent parsing
is not; the missing half is carried as §8.3a open.
## §9 Lifecycle
§8.3a W3C traceparent parsing
Spec: §8.3 — an incoming traceparent header is parsed and the span is
attached to it.
Repo: —
Tests: —
Status: open — the traceparent half of §8.3. No test and no occurrence in
the tree names traceparent.
§9.1 Startup sequence
Spec: §9.1 — config → TLS → DCPS → reader/writer + FileDescriptor → HTTP/2 bind → signal handler.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs,
crates/grpc-bridge/src/server.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§9.2 Shutdown SIGTERM/SIGINT/SIGHUP
Spec: §9.2 — graceful drain max 30 s, GOAWAY, RPCs end naturally; SIGHUP TLS+ACL reload.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::handle_connection
(every connection checks the stop flag every 200 ms, a call being served
finishes first, then GOAWAY with the last answered stream id and NO_ERROR;
the idle close after 5 s sends GOAWAY too), ::apply_reload (TLS material,
auth, ACL; a file that does not load changes nothing).
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::sigterm_sends_goaway_with_the_last_stream_and_exits_0,
::a_sighup_applies_the_new_token_and_a_broken_file_keeps_it,
::a_sighup_serves_the_next_connection_with_the_new_certificate.
Status: done
§10 Cross-vendor
§10 RTPS peer + grpc-go/java/grpcurl/ghz/Bloom-RPC/tonic
Spec: §10 — the daemon is a normal RTPS peer; the gRPC side against grpc-go/java/grpcurl/ghz/Bloom-RPC/tonic.
Repo: crates/grpc-bridge/src/server.rs.
Tests: crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_list_services_against_zerodds_daemon,
crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_lists_the_reflection_service_itself (both #[ignore], gated
on GRPC_GRPCURL=1, feature cross-vendor-tests),
crates/grpc-bridge/tests/cross_vendor.rs::list_services_reflection_payload_is_well_formed;
crates/grpc-bridge/tests/dds_bridge_e2e.rs::dds_publish_then_subscribe_roundtrip
for the RTPS side. grpc-go, grpc-java, ghz, BloomRPC and tonic have not been
run against the daemon — see §10a.
Status: partial — grpcurl is measured, the five other clients are not; the
missing part is carried as §10a open.
### §10a Client matrix grpc-go/grpc-java/ghz/BloomRPC/tonic
Spec: §10 — the daemon is driven by grpc-go, grpc-java, ghz, BloomRPC and tonic.
Repo: —
Tests: —
Status: open — the part of §10 and §12.3 beyond grpcurl.
crates/grpc-bridge/tests/cross_vendor.rs knows only grpcurl.
§11 Packaging
§11 Linux/macOS/Windows/Docker layout
Spec: §11 — binary zerodds-grpc-bridged; configs/services/Docker;
manuals.
Repo: packaging/linux/systemd/zerodds-grpc-bridged.service,
packaging/macos/launchd/org.zerodds.grpc-bridged.plist,
packaging/macos/homebrew/zerodds-grpc-bridge.rb,
packaging/windows/services/Install-Services.ps1,
packaging/docker/grpc-bridged/,
packaging/linux/configs/grpc-bridged.yaml.example,
man/man1/zerodds-grpc-bridged.1, man/man5/zerodds-grpc-bridged.yaml.5.
Tests: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::tests::{example_yaml_parses_without_error, example_yaml_key_fields_match_intent, example_yaml_topics_reach_the_daemon, example_yaml_has_no_unknown_top_level_keys},
crates/config/tests/layering.rs::a_hash_inside_quotes_is_part_of_the_value
(the shipped example configuration through the real parser, with the values it states);
tools/traceability/tests/bridge_config_manpages.rs::{every_example_key_is_documented_in_the_man_page, the_man_page_names_no_key_the_example_does_not_declare, the_pre_spec_vocabulary_is_gone_from_pages_and_examples, every_man_page_names_its_parser_and_what_an_unknown_key_does}
(the manual page and the example file name the same keys).
Status: done
§12 Testing
§12.1 Unit tests per module
Spec: §12.1 — config/http2_codec/hpack/grpc_status/reflection/dds_pump/auth, ≥ 5 tests each.
Repo: crates/grpc-bridge/src/{frame.rs,metadata.rs,path.rs,server.rs,status.rs,timeout.rs}
plus crates/http2/, crates/hpack/.
Tests: Inline #[cfg(test)] mod tests per module:
crates/grpc-bridge/src/frame.rs::tests::round_trip_message (9),
crates/grpc-bridge/src/metadata.rs::tests::decode_base64_round_trip (18),
crates/grpc-bridge/src/path.rs::tests::parses_standard_grpc_path (7),
crates/grpc-bridge/src/server.rs::tests::encode_response_includes_status_and_trailers (4),
crates/grpc-bridge/src/status.rs::tests::all_17_codes_round_trip_via_numeric (5),
crates/grpc-bridge/src/timeout.rs::tests::timeout_unit_round_trip_for_all (11),
crates/grpc-bridge/src/qos_translation.rs::tests::default_for_topic_reliable_only (7);
plus crates/grpc-bridge/tests/fuzz_smoke.rs (fuzz_decode_message_no_panic,
fuzz_parse_path_no_panic, fuzz_decode_timeout_no_panic,
empty_inputs_no_panic, message_oversized_length_no_oom).
Status: done
§12.2 Integration tests bridge_e2e
Spec: §12.2 — spawn the daemon, grpcurl as the client, byte-exact round-trip + reflection.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic,
::http2_unknown_service_yields_status_5.
Status: done
§12.3 Multi-vendor cross_vendor.rs
Spec: §12.3 — a tonic client + a Cyclone-DDS subscriber + the ZeroDDS gRPC bridge in compose.
Repo: crates/grpc-bridge/tests/cross_vendor.rs (cluster-C cross-vendor
harness).
Tests: crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_list_services_against_zerodds_daemon,
crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_lists_the_reflection_service_itself,
crates/grpc-bridge/tests/cross_vendor.rs::list_services_reflection_payload_is_well_formed;
crates/grpc-bridge/tests/dds_bridge_e2e.rs::dds_publish_then_subscribe_roundtrip.
No tonic client appears in it — see §10a.
Status: partial — grpcurl and the DDS side are covered, a tonic client is not. ## §13 Cross-references
§13 Related library + standards + daemons
Spec: §13 — library crates/grpc-bridge//crates/http2//crates/hpack/,
gRPC-PROTOCOL.md, RFC 7540/7541, wire format, deployment, sister daemons.
Repo: —
Tests: —
Status: n/a (informative) — reference list.
§14 Versioning
§14 SemVer bump rules
Spec: §14 — patch=bugfixes, minor=additive config/.proto, major=wire-protocol change (HTTP/3 + gRPC-over-QUIC).
Repo: —
Tests: —
Status: n/a (informative) — rule for assigning the version number of a release, not a property of the daemon.
Audit status
20 done / 3 partial / 3 open / 0 blocked / 3 n/a (informative).
Open items: zerodds-grpc-bridge-1.0.open.en.md.
zerodds-grpc-bridge v1.0 — Spec-Coverage
Quelle: docs/specs/zerodds-grpc-bridge-1.0.md
Implementation:
crates/grpc-bridge/· docs.rs — DDS↔︎gRPC-Bridge.
§1 Conformance-Levels
§1 L1-L6 Conformance-Matrix
Spec: §1 — sechs Levels (Wire/DDS/Bridging/Config/Auth/Multi- Tenant); L1–L4 Pflicht, L5–L6 optional.
Repo: —
Tests: —
Status: n/a (informative) — Zuordnung der Levels; die Anforderungen sind die Items §2 bis §12.
§2 CLI-Surface
§2 zerodds-grpc-bridged CLI
Spec: §2 — Optionen --config/--bind/--domain/--tls-*/
--reflection/--topic/--log-level/--metrics/--version/--help;
Exit-Codes 0/1/2/3/4.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic,
::http2_unknown_service_yields_status_5.
Status: done
§3 Config-File-Format
§3 YAML-Loader mit ENV-Substitution
Spec: §3 — Top-Level domain/grpc/auth/topics/metrics;
ENV-Substitution.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs (Config-
Parser), crates/grpc-bridge/src/server.rs.
Tests: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::tests: config_default_has_default_bind,
parse_minimal_grpc_config, parse_topics_array, parse_invalid_yaml_returns_err,
example_yaml_parses_without_error, example_yaml_key_fields_match_intent,
example_yaml_topics_reach_the_daemon, example_yaml_has_no_unknown_top_level_keys,
unknown_top_level_key_does_not_abort_the_parse;
crates/config/tests/layering.rs::a_hash_inside_quotes_is_part_of_the_value; die ENV-Substitution prüft
crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::tests::env_substitution_applies_before_the_parse.
Status: done
§4 gRPC-Wire-Protocol
§4.1 HTTP/2-Setup mit ALPN h2
Spec: §4.1 — HTTP/2-Server, ALPN h2, Cleartext-h2c nur Dev-Mode
(bind=127.0.0.1); SETTINGS-Frame, GOAWAY bei Shutdown.
Repo: crates/grpc-bridge/src/server.rs,
crates/grpc-bridge/src/frame.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§4.2 Service-Definition Auto-Generation pro Topic
Spec: §4.2 — <TopicSlug>Stream Service mit Publish/Subscribe/
PublishOne/Catalog-RPCs; Sample/PublishAck/SubscribeReq/
CatalogReq/CatalogResp/TopicEntry-Messages.
Repo: crates/grpc-bridge/src/path.rs (Slug→Service-Name),
crates/grpc-bridge/src/service_gen.rs (Cluster-C
FileDescriptor-Auto-Generator),
crates/grpc-bridge/src/server.rs.
Tests: crates/grpc-bridge/src/service_gen.rs::tests: pascal_simple,
pascal_dotted, pascal_mixed_separators, pascal_empty_falls_back,
topic_service_paths, matches_path_publish, render_proto_contains_methods,
catalog_registration_dedup, catalog_find_by_path,
catalog_fully_qualified_names;
crates/grpc-bridge/src/path.rs::tests::parses_standard_grpc_path,
crates/grpc-bridge/src/path.rs::tests::parses_dotted_service_name;
crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§4.3 RPC-Flows Publish/Subscribe
Spec: §4.3 — HEADERS/DATA/HEADERS-END_STREAM-Flow mit :method=POST,
:path=/<pkg>.<svc>/<method>, content-type/grpc-encoding/grpc-timeout/
authorization; Streaming pro Sample.
Repo: crates/grpc-bridge/src/server.rs,
crates/grpc-bridge/src/metadata.rs,
crates/grpc-bridge/src/timeout.rs. L2-DDS-Anbindung (Feature
dds-runtime): crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs
(mod bridge_dds) — Publish schreibt die Sample.payload via
DcpsRuntime::write_user_sample_borrowed auf einen echten DataWriter,
Subscribe drained den DataReader-Channel (UserSample::Alive). Ersetzt
den vormaligen FUTURE (L2)-Echo-Stub.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic;
crates/grpc-bridge/tests/dds_bridge_e2e.rs::dds_publish_then_subscribe_roundtrip
(gRPC Publish → DataWriter → Loopback-DataReader → gRPC Subscribe,
byte-gleiche Payload durch echtes DDS).
Status: done
F2b — Java-Multi-Process-Bridge: Der pure-Java gRPC-Client
org.zerodds.bridge.GrpcBridgeClient(crates/java-omgdds/java) spricht dieselbe gRPC-Wire (minimaler raw-HTTP/2-Framer über Socket, da der Daemon prior-knowledge-h2c ist). Java↔︎Rust-e2eGrpcBridgeClientTest+crates/java-omgdds/run_grpc_bridge_e2e.sh(3/3 grün): Java publisht über gRPC → DDS → Java subscribt zurück. Realisiert den listener-callbacks §7.3 „gRPC-Bridge-Pfad (pure-Java per Vendor-Extension)“.
§4.4 Length-Prefix-Wrapper
Spec: §4.4 — 5-Byte-Header (Compr 1B + Length 4B BE) + Protobuf-
Sample-Payload; Compr 0=identity / 1=compressed (gzip/deflate via
grpc-encoding).
Repo: crates/grpc-bridge/src/frame.rs.
Tests: Inline #[cfg(test)] mod tests in crates/grpc-bridge/src/frame.rs;
crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§4.5 Status-Mapping OK/INVALID_ARGUMENT/PERMISSION_DENIED/…
Spec: §4.5 — gRPC-Status-Codes 0/3/4/7/8/13/14/16 → DDS-Bedingungen.
Repo: crates/grpc-bridge/src/status.rs.
Tests: Inline #[cfg(test)] mod tests in crates/grpc-bridge/src/status.rs;
crates/grpc-bridge/tests/bridge_e2e.rs::http2_unknown_service_yields_status_5.
Status: done
§4.6 Reflection-Service grpc.reflection.v1alpha
Spec: §4.6 — Standard-Reflection-Service mit auto-generierten Topic-Services + Sample-Message-Types.
Repo: crates/grpc-bridge/src/server.rs (Reflection-Hook),
crates/grpc-bridge/src/reflection.rs (Cluster-C Reflection-RPC
voll wired).
Tests: crates/grpc-bridge/src/reflection.rs::tests:
empty_catalog_yields_empty_list_services_response, one_service_in_list,
error_response_encodes_code_and_message, varint_basics,
reflection_path_constant_matches_grpcurl_default;
crates/grpc-bridge/tests/cross_vendor.rs::list_services_reflection_payload_is_well_formed sowie die beiden
grpcurl-Läufe crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_list_services_against_zerodds_daemon und
crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_lists_the_reflection_service_itself (#[ignore], Gate
GRPC_GRPCURL=1).
Status: done
§5 Topic-Mapping
§5.1 Service-Namen-Default Topic→ChatMessageStream
Spec: §5.1 — ::-Split + CamelCase + Suffix Stream; Package
zerodds.<segment>.v1.
Repo: crates/grpc-bridge/src/path.rs.
Tests: crates/grpc-bridge/src/service_gen.rs::tests::pascal_simple,
crates/grpc-bridge/src/service_gen.rs::tests::pascal_dotted,
crates/grpc-bridge/src/service_gen.rs::tests::pascal_mixed_separators,
crates/grpc-bridge/src/service_gen.rs::tests::pascal_empty_falls_back,
crates/grpc-bridge/src/service_gen.rs::tests::topic_service_paths;
crates/grpc-bridge/src/path.rs::tests (7 Tests) prüft die Pfad-Form
selbst.
Status: done
§5.2 Type-Discovery Reflection + Catalog + IDL-Endpoint
Spec: §5.2 — drei Pfade (Reflection-Service, Catalog-RPC, HTTP-GET-Sidekick).
Repo: crates/grpc-bridge/src/server.rs,
crates/grpc-bridge/src/reflection.rs,
crates/grpc-bridge/src/service_gen.rs (Cluster-C Type-Discovery via
Reflection + Catalog-RPC + HTTP-GET-Sidekick).
Tests: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::tests::build_catalog_emits_topics,
crates/grpc-bridge/src/daemon_runtime.rs::tests::catalog_render_json_is_valid_shape;
crates/grpc-bridge/src/service_gen.rs::tests::catalog_registration_dedup,
::catalog_find_by_path, ::catalog_fully_qualified_names;
crates/grpc-bridge/src/reflection.rs::tests::one_service_in_list.
Status: done
§6 QoS-Translation
§6 DDS-QoS → gRPC-Behavior Map
Spec: §6 — Reliability/Durability/History/Lifespan/Deadline/
Liveliness/Partition Map; partition_filter als Subscribe-Field.
Repo: crates/grpc-bridge/src/server.rs,
crates/grpc-bridge/src/timeout.rs,
crates/grpc-bridge/src/metadata.rs,
crates/grpc-bridge/src/qos_translation.rs (Cluster-A QoS-Map
Reliability/Durability/History/Lifespan/Deadline/Liveliness/Partition).
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic
(reliable Roundtrip); QoS-Matrix in
crates/grpc-bridge/src/qos_translation.rs::tests.
Status: done
§7 Security
§7.1 TLS h2-ALPN + Cert-Rotation
Spec: §7.1 — TLS 1.2+ (1.3 default), ALPN h2, SIGHUP-Cert-Rotation.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::serve_tls,
::security_from, ::apply_reload;
crates/grpc-bridge/src/daemon_runtime.rs::build_grpc_tls_config_with
(grpc.tls.client_ca_file, grpc.tls.require_client_cert).
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::mtls_serves_h2_and_the_certificate_is_the_subject
(ALPN h2 am Client gelesen),
::a_sighup_serves_the_next_connection_with_the_new_certificate;
gegen grpc-go crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_over_tls_with_a_bearer_token
(grpcurl 1.9.3, Linux-Testhost);
crates/grpc-bridge/tests/security_e2e.rs::build_grpc_tls_config_sets_alpn_h2,
::build_grpc_tls_config_with_client_ca_for_mtls, ::build_grpc_tls_config_rejects_missing_cert.
Status: done
§7.2 Auth-Modes none/jwt/mtls
Spec: §7.2 — JWT-Bearer-Authorization-Metadata, mTLS-Cert-DN; §3
auth.jwt.public_key, audience, required_claim.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::handle_connection
(Prüfung pro Call auf der authorization-Metadata, UNAUTHENTICATED),
::security_from; crates/grpc-bridge/src/server.rs (GrpcRequest::metadata);
crates/bridge-security/src/auth.rs (RS256, exp/nbf mit 60 s Toleranz,
aud, geforderter Claim; jwt_public_key_from_pem für PKCS#1 und SPKI).
Zusätzlich bearer mit auth.bearer_token/auth.bearer_subject. Das
mTLS-Subject ist der Subject-DN des Zertifikats (RFC 4514, CN=alice);
eine ACL-Regel darf stattdessen den Fingerprint mtls:<SHA-256> nennen.
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::bearer_and_acl_are_checked_per_call,
::jwt_checks_signature_time_audience_and_scope,
::mtls_serves_h2_and_the_certificate_is_the_subject,
::jwt_without_a_key_and_mtls_without_tls_do_not_start;
crates/bridge-security/src/auth.rs::tests::a_signed_jwt_is_checked_for_time_audience_and_claim.
Status: done
§7.3 Per-Topic-ACL
Spec: §7.3 — acl.publish/acl.subscribe Listen mit Subject-
Resolution.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::dispatch
(Publish = Schreiben, Subscribe = Lesen auf dem DDS-Topic,
PERMISSION_DENIED); topics[].acl in DaemonConfig::from_config.
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::bearer_and_acl_are_checked_per_call,
::jwt_checks_signature_time_audience_and_scope (JWT-sub als Subject),
::mtls_serves_h2_and_the_certificate_is_the_subject (Zertifikat als Subject).
Status: done
§8 Operations + Observability
§8.1 Strukturiertes JSON-Logging
Spec: §8.1 — JSON-Log + --log-level-Switch.
Repo: —
Tests: —
Status: open — kein Test prüft die JSON-Log-Form oder den --log-level-Switch.
### §8.2 Prometheus-Metrics
Spec: §8.2 — --metrics-CLI + 10 Counter/Gauge-Familien.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::SpecMetrics
(alle zehn zerodds_grpc_bridge_*-Familien). Labelwerte nur aus der
Konfiguration oder einer festen Menge: ein nicht gemappter Service zählt als
service="unknown", eine fremde Methode als method="other".
bytes_in/bytes_out zählen die Length-Prefixed-Message samt 5-Oktett-
Präfix; h2_resets_total zählt empfangene RST_STREAM nach RFC-7540-§7-Namen
(der Daemon sendet keine). Die älteren zerodds_grpc_* aus
crates/grpc-bridge/src/daemon_runtime.rs bleiben daneben.
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::the_metrics_of_section_8_2_count_calls_failures_and_resets
(Status pro Call, Auth-Fehler nach Grund, RST_STREAM(CANCEL), mit
dds-runtime Publish → DDS → Subscribe);
crates/grpc-bridge/src/daemon_runtime.rs::tests::admin_endpoint_serves_metrics_path.
Status: done
§8.3 OTLP-Spans + W3C-Traceparent
Spec: §8.3 — OTEL_EXPORTER_OTLP_ENDPOINT + Trace-Propagation per
Traceparent-Header.
Repo: crates/grpc-bridge/src/metadata.rs (Traceparent-Parsing-
Hook), crates/grpc-bridge/src/daemon_runtime.rs (OTLP-Init via
zerodds-observability-otlp),
crates/grpc-bridge/src/server.rs (Span-Emit pro RPC).
Tests: crates/grpc-bridge/src/daemon_runtime.rs::tests::otlp_config_parses_endpoint — der Endpoint aus
OTEL_EXPORTER_OTLP_ENDPOINT wird gelesen. Das Traceparent-Parsing prüft
kein Test — siehe §8.3a.
Status: partial — die OTLP-Konfiguration ist geprüft, das W3C-Traceparent-Parsing
nicht; der fehlende Teil wird als §8.3a open geführt.
## §9 Lifecycle
§8.3a W3C-Traceparent-Parsing
Spec: §8.3 — ein eingehender traceparent-Header wird geparst und der
Span daran gehängt.
Repo: —
Tests: —
Status: open — der Traceparent-Teil von §8.3. Kein Test und keine
Fundstelle im Baum nennt traceparent.
§9.1 Startup-Sequence
Spec: §9.1 — Config → TLS → DCPS → Reader/Writer + FileDescriptor → HTTP/2-Bind → Signal-Handler.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs,
crates/grpc-bridge/src/server.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic.
Status: done
§9.2 Shutdown SIGTERM/SIGINT/SIGHUP
Spec: §9.2 — Graceful Drain max 30 s, GOAWAY, RPCs natürlich beenden; SIGHUP TLS+ACL-Reload.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::handle_connection
(jede Verbindung prüft das Stop-Flag alle 200 ms, ein laufender Call endet
zuerst, dann GOAWAY mit der letzten beantworteten Stream-ID und NO_ERROR;
auch der Idle-Abschluss nach 5 s sendet GOAWAY), ::apply_reload
(TLS-Material, Auth, ACL; eine Datei, die nicht lädt, ändert nichts).
Tests: crates/grpc-bridge/tests/daemon_security_e2e.rs::sigterm_sends_goaway_with_the_last_stream_and_exits_0,
::a_sighup_applies_the_new_token_and_a_broken_file_keeps_it,
::a_sighup_serves_the_next_connection_with_the_new_certificate.
Status: done
§10 Cross-Vendor
§10 RTPS-Peer + grpc-go/java/grpcurl/ghz/Bloom-RPC/tonic
Spec: §10 — Daemon ist normaler RTPS-Peer; gRPC-Seite gegen grpc-go/java/grpcurl/ghz/Bloom-RPC/tonic.
Repo: crates/grpc-bridge/src/server.rs.
Tests: crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_list_services_against_zerodds_daemon,
crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_lists_the_reflection_service_itself (beide #[ignore], Gate
GRPC_GRPCURL=1, Feature cross-vendor-tests),
crates/grpc-bridge/tests/cross_vendor.rs::list_services_reflection_payload_is_well_formed;
crates/grpc-bridge/tests/dds_bridge_e2e.rs::dds_publish_then_subscribe_roundtrip
für die RTPS-Seite. grpc-go, grpc-java, ghz, BloomRPC und tonic hat niemand
gegen den Daemon laufen lassen — siehe §10a.
Status: partial — grpcurl ist gemessen, die fünf weiteren Clients nicht; der
fehlende Teil wird als §10a open geführt.
### §10a Client-Matrix grpc-go/grpc-java/ghz/BloomRPC/tonic
Spec: §10 — der Daemon wird gegen grpc-go, grpc-java, ghz, BloomRPC und tonic gefahren.
Repo: —
Tests: —
Status: open — der Teil von §10 und §12.3 jenseits grpcurl.
crates/grpc-bridge/tests/cross_vendor.rs kennt nur grpcurl.
§11 Packaging
§11 Linux/macOS/Windows/Docker Layout
Spec: §11 — Binary zerodds-grpc-bridged; Configs/Services/Docker;
Manuals.
Repo: packaging/linux/systemd/zerodds-grpc-bridged.service,
packaging/macos/launchd/org.zerodds.grpc-bridged.plist,
packaging/macos/homebrew/zerodds-grpc-bridge.rb,
packaging/windows/services/Install-Services.ps1,
packaging/docker/grpc-bridged/,
packaging/linux/configs/grpc-bridged.yaml.example,
man/man1/zerodds-grpc-bridged.1,
man/man5/zerodds-grpc-bridged.yaml.5.
Tests: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs::tests::{example_yaml_parses_without_error, example_yaml_key_fields_match_intent, example_yaml_topics_reach_the_daemon, example_yaml_has_no_unknown_top_level_keys},
crates/config/tests/layering.rs::a_hash_inside_quotes_is_part_of_the_value
(die ausgelieferte Beispiel-Konfiguration durch den echten Parser, mit den Werten, die sie nennt);
tools/traceability/tests/bridge_config_manpages.rs::{every_example_key_is_documented_in_the_man_page, the_man_page_names_no_key_the_example_does_not_declare, the_pre_spec_vocabulary_is_gone_from_pages_and_examples, every_man_page_names_its_parser_and_what_an_unknown_key_does}
(die Handbuchseite und die Beispieldatei nennen dieselben Schlüssel).
Status: done
§12 Testing
§12.1 Unit-Tests pro Modul
Spec: §12.1 — config/http2_codec/hpack/grpc_status/reflection/ dds_pump/auth je ≥ 5 Tests.
Repo: crates/grpc-bridge/src/{frame.rs,metadata.rs,path.rs,server.rs,status.rs,timeout.rs} plus crates/http2/, crates/hpack/.
Tests: Inline #[cfg(test)] mod tests pro Modul:
crates/grpc-bridge/src/frame.rs::tests::round_trip_message (9),
crates/grpc-bridge/src/metadata.rs::tests::decode_base64_round_trip (18),
crates/grpc-bridge/src/path.rs::tests::parses_standard_grpc_path (7),
crates/grpc-bridge/src/server.rs::tests::encode_response_includes_status_and_trailers (4),
crates/grpc-bridge/src/status.rs::tests::all_17_codes_round_trip_via_numeric (5),
crates/grpc-bridge/src/timeout.rs::tests::timeout_unit_round_trip_for_all (11),
crates/grpc-bridge/src/qos_translation.rs::tests::default_for_topic_reliable_only (7);
dazu crates/grpc-bridge/tests/fuzz_smoke.rs (fuzz_decode_message_no_panic,
fuzz_parse_path_no_panic, fuzz_decode_timeout_no_panic,
empty_inputs_no_panic, message_oversized_length_no_oom).
Status: done
§12.2 Integration-Tests bridge_e2e
Spec: §12.2 — Spawn Daemon, grpcurl als Client, byte-genauer Roundtrip + Reflection.
Repo: crates/grpc-bridge/src/bin/zerodds-grpc-bridged.rs.
Tests: crates/grpc-bridge/tests/bridge_e2e.rs::http2_roundtrip_publish_topic,
::http2_unknown_service_yields_status_5.
Status: done
§12.3 Multi-Vendor cross_vendor.rs
Spec: §12.3 — tonic-Client + Cyclone-DDS-Subscriber + ZeroDDS- gRPC-Bridge im Compose.
Repo: crates/grpc-bridge/tests/cross_vendor.rs (Cluster-C
Cross-Vendor-Harness).
Tests: crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_list_services_against_zerodds_daemon,
crates/grpc-bridge/tests/cross_vendor.rs::grpcurl_lists_the_reflection_service_itself,
crates/grpc-bridge/tests/cross_vendor.rs::list_services_reflection_payload_is_well_formed;
crates/grpc-bridge/tests/dds_bridge_e2e.rs::dds_publish_then_subscribe_roundtrip.
Ein tonic-Client kommt darin nicht vor — siehe §10a.
Status: partial — grpcurl und die DDS-Seite sind geprüft, ein tonic-Client nicht. ## §13 Cross-References
§13 Verwandte Library + Standards + Daemons
Spec: §13 — Library crates/grpc-bridge//crates/http2//
crates/hpack/, gRPC-PROTOCOL.md, RFC 7540/7541, Wire-Format,
Deployment, Sister-Daemons.
Repo: —
Tests: —
Status: n/a (informative) — Verweisliste.
§14 Versioning
§14 SemVer-Bump-Regeln
Spec: §14 — Patch=Bugfixes, Minor=additive Config/.proto, Major= Wire-Protocol-Change (HTTP/3 + gRPC-over-QUIC).
Repo: —
Tests: —
Status: n/a (informative) — Regel für die Vergabe der Versionsnummer eines Release, keine Eigenschaft des Daemons.
Audit-Status
20 done / 3 partial / 3 open / 0 blocked / 3 n/a (informative).
Offene Punkte: zerodds-grpc-bridge-1.0.open.md.