experiment: google.api.http annotations on CommandService (Peaceful Studio internal) - #1
Closed
monsieurleberre wants to merge 2 commits into
Closed
experiment: google.api.http annotations on CommandService (Peaceful Studio internal)#1monsieurleberre wants to merge 2 commits into
monsieurleberre wants to merge 2 commits into
Conversation
…e options Demonstrates Option 4 from the C# SDK proposal appendix: annotate Canton's .proto files with google.api.http so the JSON HTTP surface can be derived from the protos directly, bypassing tapir's translation layer. Scope (one service end-to-end): - community/ledger-api-proto/.../v2/command_service.proto: imports google/api/annotations.proto and adds an option (google.api.http) block to each of the three RPCs (SubmitAndWait, SubmitAndWaitForTransaction, SubmitAndWaitForReassignment), matching the existing tapir routes. - All v2 .proto files (22) plus value.proto: add option go_package next to the existing csharp_namespace/java_package options. Required by every off-the-shelf OpenAPI generator in the ecosystem. Harmless to existing C#/Java/Scala codegen, which does not read this option. Verified with `buf build`: clean compile. Verified end-to-end OpenAPI generation with buf.build/grpc-ecosystem/openapiv2:v2.28.0: the output fixes all four type-system defects catalogued in the C# SDK proposal (numbered duplicate schemas, single-key oneOf wrappers, inline duplicated enums, untyped DAML payload fields). See peacefulstudio/documentation/dev-funding/openApi/upstream-fork-experiment.md for the full write-up.
Adds review-aid artefacts proving:
- go_package additions are inert for existing Canton consumers
- source diff: one added line per file, sibling to existing
csharp_namespace / java_package / java_outer_classname options
- compiled FileDescriptorSet diff (source_code_info stripped):
one added line `go_package: "..."` inside `options {}`, nothing
else changes
- canton-build-targets.txt: every PB.targets in BuildCommon.scala
routes to scalapb.gen or PB.gens.java; no Go plugin invoked
anywhere in the build, so go_package cannot be read
- the annotated proto produces a clean OpenAPI
- buf.gen.yaml + 1,372-line command_service.swagger.json
- the "after" side of the SwaggerHub comparison linked from
the PR description
Internal to the Peaceful Studio fork. To be deleted before any
upstream PR to digital-asset/canton.
This was referenced May 11, 2026
Author
monsieurleberre
added a commit
that referenced
this pull request
Jul 26, 2026
…nAPI (#2) * experiment: google.api.http annotations on CommandService + go_package options Demonstrates Option 4 from the C# SDK proposal appendix: annotate Canton's .proto files with google.api.http so the JSON HTTP surface can be derived from the protos directly, bypassing tapir's translation layer. Scope (one service end-to-end): - community/ledger-api-proto/.../v2/command_service.proto: imports google/api/annotations.proto and adds an option (google.api.http) block to each of the three RPCs (SubmitAndWait, SubmitAndWaitForTransaction, SubmitAndWaitForReassignment), matching the existing tapir routes. - All v2 .proto files (22) plus value.proto: add option go_package next to the existing csharp_namespace/java_package options. Required by every off-the-shelf OpenAPI generator in the ecosystem. Harmless to existing C#/Java/Scala codegen, which does not read this option. Verified with `buf build`: clean compile. Verified end-to-end OpenAPI generation with buf.build/grpc-ecosystem/openapiv2:v2.28.0: the output fixes all four type-system defects catalogued in the C# SDK proposal (numbered duplicate schemas, single-key oneOf wrappers, inline duplicated enums, untyped DAML payload fields). See peacefulstudio/documentation/dev-funding/openApi/upstream-fork-experiment.md for the full write-up. * experiment: extend google.api.http coverage to every JSON-API endpoint Builds on the CommandService experiment by annotating every other v2 RPC the JSON API exposes over HTTP, plus adding go_package to the admin/, testing/, and interactive/ sub-packages so off-the-shelf OpenAPI generators can resolve the full proto closure. Annotated services (route source: community/ledger/ledger-json-api Js*Service.scala): v2/ - CommandSubmissionService: Submit, SubmitReassignment - CommandCompletionService: CompletionStream - ContractService: GetContract - EventQueryService: GetEventsByContractId - PackageService: ListPackages, GetPackage, GetPackageStatus, ListVettedPackages - StateService: GetActiveContracts, GetActiveContractsPage, GetConnectedSynchronizers, GetLedgerEnd, GetLatestPrunedOffsets - UpdateService: GetUpdates, GetUpdateByOffset, GetUpdateById, GetUpdatesPage - VersionService: GetLedgerApiVersion v2/admin/ - IdentityProviderConfigService: 5 RPCs - PackageManagementService: UploadDarFile, ValidateDarFile, UpdateVettedPackages - PartyManagementService: 7 HTTP-exposed RPCs - UserManagementService: 9 RPCs v2/interactive/ - InteractiveSubmissionService: 6 RPCs Each annotation matches the existing tapir route (method + path + body mapping) so the proto-derived OpenAPI is comparable to the current tapir-emitted document. Scope notes: - Server-streaming RPCs (CompletionStream, GetActiveContracts, GetUpdates) get only the canonical POST/list mapping; the WebSocket GET form is a JSON-API-only convenience that has no representation in google.api.http and remains in the AsyncAPI doc. - Legacy JSON-only paths (/v2/updates/flats|trees|transaction-*, /v2/authenticated-user, /v2/definitions/*) are not annotated — they either lack a 1:1 proto RPC or are non-gRPC custom endpoints. - Non-HTTP-exposed RPCs (TimeService, CommandInspectionService, ParticipantPruningService, PackageManagementService.ListKnownPackages, PartyManagementService.UpdatePartyIdentityProviderId) are deliberately left unannotated. Verified locally with `buf build community/ledger-api-proto/src/main/protobuf`: clean compile. * experiment: switch GetActiveContractsPage to POST The JSON API exposes this as GET with a JSON body containing a required EventFormat message — non-standard REST, and rejected by grpc-ecosystem/openapiv2 ("must not set request body when http method is GET"). Switching to POST is the canonical mapping for a request with a non-trivial body, and is the single intentional deviation from the existing tapir route for the proto-derived OpenAPI. * http-annotations-evidence: burden-of-evidence for full-coverage PR Mirrors the experiment-evidence pattern from the single-service experiment, scaled to the full HTTP API: - openapi-3.0/openapi.yaml — OpenAPI 3.0.3 YAML generated via buf.build/community/google-gnostic-openapi:v0.7.0; same major version as the existing community/ledger/ledger-json-api/.../openapi.yaml, so the two can be diffed directly. - swagger-2.0/canton-ledger-api.swagger.yaml — Swagger 2.0 YAML via buf.build/grpc-ecosystem/openapiv2:v2.28.0; same plugin as the upstream CommandService experiment, merged across every annotated service. - coverage-comparison/ — sorted path inventories and the diff between the JSON API spec and the proto-derived spec, after normalizing path-parameter names (proto field names ↔ tapir placeholder names). 9 paths-only-in-JSON-API (all deliberate skips: 1 custom JSON-only endpoint + 8 deprecated legacy aliases), 0 paths-only-in-proto. - reproduce.sh — one-shot regen of every artefact in this directory. Internal to the Peaceful Studio fork. To be deleted before any upstream PR to digital-asset/canton. * experiment-evidence: fold #1 evidence bundle into the full-coverage branch Carries over the go_package-harmlessness proof bundle (before/after descriptor diffs showing the option is inert for existing codegen) and the CommandService openapi POC from #1, so this branch is self-contained once #1 closes as superseded.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Side-by-side rendered comparison (SwaggerHub)
CommandService.SubmitAndWaitpostV2CommandsSubmit-and-waitOpen both, compare the request/response schemas at the bottom of each operation. The "before" spec references
JsCommands(which transitively pulls in numbered duplicates, single-keyoneOfenvelopes, and inline-duplicated enums); the "after" spec referencescom.daml.ledger.api.v2.Commandsdirectly with proper proto-qualified types throughout.Summary
Proof-of-concept for the upstream fix outlined as Option 4 of the C# SDK funding proposal: annotate Canton's
.protofiles withgoogle.api.httpso the JSON HTTP surface can be regenerated cleanly from the protos, bypassing the tapir translation layer that introduces the structural defects catalogued indocumentation/dev-funding/openApi/why-openapi-is-not-enough.md.Scope: one service end-to-end (
CommandService, 3 RPCs) +go_packageoptions on the 22 v2 protos +value.proto.Not for upstream submission yet — internal Peaceful Studio review only. See
documentation/dev-funding/openApi/upstream-fork-experiment.mdfor the full write-up, proposed upstream-PR split, CLA notes, and verification details.What's in this PR
command_service.proto:import "google/api/annotations.proto";+option (google.api.http) = {...}on each of the 3 RPCs/v2/commands/submit-and-wait,submit-and-wait-for-transaction,submit-and-wait-for-reassignment).value.proto:option go_package = "github.com/digital-asset/canton/go/ledger/api/v2;ledgerapiv2";Built and verified with the canton dependency stack as-is. No build-system changes required for the annotation parsing step.
Verification (reproducible end-to-end)
The generated OpenAPI (1,372 lines) demonstrably fixes all four type-system defects from
why-openapi-is-not-enough.md:Empty1–Empty10): types are qualified by proto package (com.daml.ledger.api.v2.Commands,com.daml.ledger.api.v2.Record).oneOfenvelopes: oneof branches emit as sibling optional properties.com.daml.ledger.api.v2.TransactionShape) and referenced everywhere.createArguments→$ref: com.daml.ledger.api.v2.Record,choiceArgument→$ref: com.daml.ledger.api.v2.Value.Out of scope for this PR (next steps)
command_submission_service.proto,command_completion_service.proto,state_service.proto,update_service.proto,package_service.proto, etc.google-common-protos-scalaincludeFilter to also generate Scala bindings forannotations.proto/http.proto, then writing aMethodDescriptor → tapir.Endpointhelper so tapir routes are derived from the annotation instead of hand-written. Feasibility analysis in the companion write-up.digital-asset/canton(PR-1 =go_packagecross-language enablement, PR-2 = annotations + tapir derivation). Not now — internal review first.