Skip to content

fix(issuer): raise SignMetadata's metadata_json size limit from 64KB to 2MiB - #535

Merged
masv3971 merged 1 commit into
SUNET:mainfrom
sirosfoundation:fix/issuer-metadata-json-size-limit
Aug 4, 2026
Merged

fix(issuer): raise SignMetadata's metadata_json size limit from 64KB to 2MiB#535
masv3971 merged 1 commit into
SUNET:mainfrom
sirosfoundation:fix/issuer-metadata-json-size-limit

Conversation

@leifj

@leifj leifj commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Symptom

apigw logs a recurring error:

failed to refresh VCI signed metadata {"error": "failed to sign metadata via issuer: rpc error: code = InvalidArgument desc = metadata_json is too large"}

Root cause

  • apigw's 55-minute VCI signed-metadata refresh ticker (StartSignedMetadataRefresherrefreshSignedMetadata at internal/apigw/apiv1/sign_metadata.go:154) calls signMetadataViaIssuer (lines 39-60), which sends the full CredentialIssuerMetadataParameters JSON (built in internal/apigw/apiv1/client.go) over gRPC to the issuer service's SignMetadata RPC.
  • That JSON includes one CredentialConfigurationsSupported entry per configured VCTM/MDDL scope, each with multi-language Display/ClaimDescription/ClaimDisplayProperties per claim (see pkg/openid4vci/issuer_metadata.go). A real deployment with ~10 configured credential-type scopes easily produces a combined metadata JSON over 64KB.
  • internal/issuer/apiv1/sign_metadata.go (previously lines 53-55) hardcoded:
    if len(req.GetMetadataJson()) > 64*1024 {
        return nil, grpcstatus.Error(codes.InvalidArgument, "metadata_json is too large")
    }
    This 64KB cap is an arbitrary application-level guard introduced in ecd66687 ("Add constrains to signmetadata."), with no rationale given for the specific figure — it was evidently only ever exercised with 1-2 scopes configured.
  • This is not gRPC's own transport-level message-size limit: no MaxRecvMsgSize/MaxSendMsgSize is configured anywhere in internal/issuer/grpcserver/service.go or the apigw gRPC client, so grpc-go's ~4MiB default applies there and isn't what's firing.

Fix

Raised the hardcoded 64KB constant to 2 MiB, extracted into a named constant maxMetadataJSONBytes with a comment explaining the rationale:

  • Kept the app-level guard (rather than removing it and relying solely on gRPC's own limit) so an oversized payload still gets a clear, specific InvalidArgument error instead of a generic gRPC transport failure.
  • Sized it to 2 MiB — generous headroom over what a realistic multi-scope deployment produces, while staying comfortably under gRPC's own 4 MiB default, so this check (not gRPC's) is the one that actually fires.

Added a boundary test (TestSignMetadata_Validation/metadata_json_exactly_at_size_limit_is_not_rejected_for_size) asserting a payload exactly at the new limit clears the size check (and instead fails one stage later at JSON parsing, since the boundary payload isn't valid JSON — proving the size gate itself let it through), alongside the existing "one byte over the limit is rejected as too large" case (updated to use the new limit).

Testing

  • go build ./... — clean, no errors.
  • go test ./internal/issuer/apiv1/... — all pass, including the new/updated boundary tests.
  • go test ./internal/apigw/apiv1/... — all pass.
  • Full make test scope (verifier, registry, apigw, issuer — both cmd/... and internal/...) — all pass.
  • go vet and gofmt -l on changed files — clean.

…to 2MiB

apigw's 55-minute VCI signed-metadata refresh ticker (StartSignedMetadataRefresher
in internal/apigw/apiv1/sign_metadata.go) calls signMetadataViaIssuer, sending the
full CredentialIssuerMetadataParameters JSON over gRPC to the issuer service's
SignMetadata RPC. That JSON includes one CredentialConfigurationsSupported entry
per configured VCTM/MDDL scope with multi-language Display/ClaimDescription/
ClaimDisplayProperties per claim (pkg/openid4vci/issuer_metadata.go), so a real
deployment with ~10 configured scopes easily produces a payload over 64KB.

internal/issuer/apiv1/sign_metadata.go hardcoded a 64KB cap on metadata_json
(added in ecd6668, "Add constrains to signmetadata.", with no rationale for the
specific figure and evidently only ever exercised with 1-2 scopes configured).
Once a deployment grows past that, apigw logs a recurring:

  failed to refresh VCI signed metadata {"error": "failed to sign metadata via
  issuer: rpc error: code = InvalidArgument desc = metadata_json is too large"}

This is a hardcoded application-level guard, not gRPC's own transport-level
message-size limit — no MaxRecvMsgSize/MaxSendMsgSize is configured anywhere in
internal/issuer/grpcserver or the apigw gRPC client, so grpc-go's ~4MiB default
applies there and isn't what's firing.

Raise the constant to 2 MiB (extracted into maxMetadataJSONBytes): generous
headroom for realistic multi-scope metadata, while staying comfortably under
gRPC's own 4 MiB default so this check — not gRPC's — is the one that fires and
gives a clear, specific error instead of a generic transport failure.

Testing: go build ./... clean; go test ./internal/issuer/apiv1/... and
./internal/apigw/apiv1/... pass; full `make test` scope (verifier, registry,
apigw, issuer cmd+internal packages) passes. Added a boundary test asserting a
payload exactly at the new limit clears the size check (fails at JSON parsing
instead, since it's not valid JSON) and one byte over is still rejected as
"too large".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

@leifj

leifj commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Independent review: verified the root-cause analysis (no other stale/hardcoded size limits elsewhere in the metadata_json path that would need the same bump), rebuilt and re-ran the full test suite from a clean worktree — build, vet, and `make test` all green, including the new boundary test. No merge conflicts against `main`. No issues found — this looks correct as-is.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Raises the Issuer service’s application-level SignMetadata request-size guard to accommodate real-world Credential Issuer Metadata payloads that exceed the previous 64KB cap, while keeping a clear InvalidArgument failure mode under the gRPC transport limit.

Changes:

  • Increased metadata_json size limit from 64KB to 2 MiB and extracted it into a named constant (maxMetadataJSONBytes) with rationale.
  • Updated/added validation tests to cover the new boundary conditions (exactly-at-limit vs. one-byte-over).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
internal/issuer/apiv1/sign_metadata.go Replaces the hardcoded 64KB guard with a documented 2 MiB constant used by SignMetadata.
internal/issuer/apiv1/sign_metadata_test.go Adds boundary payload setup and a test case asserting “exactly at limit” passes the size gate while “over limit” is rejected.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@masv3971
masv3971 merged commit 81127e0 into SUNET:main Aug 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants