Skip to content

Phase 2: secure OIDC auth and PostgreSQL task persistence - #2

Merged
KB01111 merged 5 commits into
mainfrom
phase/2-auth-postgres
Aug 4, 2026
Merged

Phase 2: secure OIDC auth and PostgreSQL task persistence#2
KB01111 merged 5 commits into
mainfrom
phase/2-auth-postgres

Conversation

@KB01111

@KB01111 KB01111 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

User description

Summary

  • add strict OIDC discovery/JWKS verification and bearer-token middleware that removes credentials before requests enter a2asrv
  • inject an authoritative tenant and collision-safe authenticated owner into A2A calls while publishing truthful OIDC security metadata in the Agent Card
  • add a tenant/subject-isolated PostgreSQL taskstore.Store with globally unique task IDs, atomic versions, CAS, keyset pagination, filtering, and A2A-compatible list shaping
  • add checksummed forward-only migrations with an advisory lock, a separate migrator command, password-file pool configuration, and startup schema verification
  • wire authenticated durable storage into the development executable while retaining the explicit Redpanda production gate for Phase 3

SDK decision

The server continues to use github.com/a2aproject/a2a-go/v2/a2asrv v2.4.0 for A2A v1.0 routing, SSE, task lifecycle, and protocol behavior. Project code owns the outer auth boundary and PostgreSQL adapter because the SDK copies request headers before interceptors, leaves tenant handling incomplete for unprefixed REST routes, and ships no PostgreSQL task-store implementation.

Security invariants

  • only signed asymmetric JWTs with configured issuer, audience, subject, tenant, expiry, and required scopes are accepted
  • Authorization, Proxy-Authorization, and Cookie are removed before SDK transport metadata is created
  • client tenant values cannot override the signed tenant claim
  • every PostgreSQL statement is scoped to the verified tenant and subject; cross-scope access returns not found
  • task IDs are globally unique to match the SDK's task-ID-keyed queues
  • database passwords are rejected in URLs and read only from bounded, permission-checked files
  • the runtime role does not run DDL; cmd/migrate applies checksummed forward-only migrations

Verification

  • go test -count=1 ./... — 122 passed without the optional database DSN
  • TEST_DATABASE_URL=postgresql://postgres@127.0.0.1:55432/bridge_a2a?sslmode=disable go test -count=1 ./internal/storage/postgres — 11 passed against PostgreSQL 17
  • go vet ./...
  • go build ./cmd/server ./cmd/migrate
  • git diff --check

The real-database suite covers migrations, persistence, global ID collisions, tenant/owner isolation, optimistic and unconditional concurrent updates, list filters/cursors/shaping, and HTTP-to-OIDC-to-PostgreSQL identity propagation. The OIDC suite uses a real TLS discovery/JWKS test server with signed RS256 tokens, rotation, cache/outage, issuer/audience/signature/algorithm failures, and unsafe URL rejection.

Windows race detection remains unavailable because this host has no GCC/CGO toolchain; Linux CI is the authoritative race gate.

Summary by Sourcery

Introduce secure OIDC-based authentication and tenant-scoped PostgreSQL task persistence, and wire them into the server and migration workflow while tightening configuration validation and tests.

New Features:

  • Add configurable OIDC authentication with JWT verification, tenant claim handling, and A2A-aware middleware and interceptors.
  • Integrate a PostgreSQL-backed task store implementing the SDK taskstore contract with tenant/subject isolation, pagination, and list shaping.
  • Provide a dedicated database migrator command with checksummed forward-only migrations and schema verification.

Enhancements:

  • Extend server configuration to support OIDC and database settings with strict validation for URLs, algorithms, scopes, and pool limits.
  • Update the HTTP server wiring to conditionally enable authentication and durable storage, enforcing auth + task store in non-development environments.
  • Document the authentication, tenant boundary, and PostgreSQL task ownership model in architecture and README docs.

Tests:

  • Add unit and integration tests for OIDC verification, JWT handling, JWKS rotation/outage behavior, and unsafe provider configurations.
  • Add unit tests covering database pool configuration, cursor encoding/decoding, migration integrity, and PostgreSQL store behavior including CAS, isolation, and listing.
  • Add end-to-end tests ensuring HTTP-to-OIDC-to-PostgreSQL identity propagation and server behavior with authenticated vs unauthenticated requests.

CodeAnt-AI Description

Secure A2A requests with OIDC and add tenant-isolated PostgreSQL task persistence

What Changed

  • A2A endpoints now require validated OIDC bearer tokens with the configured issuer, audience, tenant, expiry, signing algorithm, and scopes.
  • Authenticated requests use the verified tenant and subject; conflicting client tenant values are rejected, and credentials are removed before request processing.
  • Tasks can be stored in PostgreSQL with tenant and owner isolation, globally unique IDs, version-checked updates, filtering, and paginated lists.
  • Added a separate migration command with checksummed, forward-only schema updates; the server verifies that the database schema is current before starting.
  • The Agent Card now publishes the OIDC discovery endpoint, while the Agent Card remains publicly accessible.
  • Production and staging require authentication and durable task storage; development and tests retain in-memory storage when PostgreSQL is not configured.

Impact

✅ Fewer unauthorized A2A requests
✅ Isolated tasks across tenants and users
✅ Durable task history across server restarts
✅ Safer database schema rollouts

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@codeant-ai

codeant-ai Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 1832e3d Aug 04, 2026 · 00:13 00:16

@codeant-ai

codeant-ai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@sourcery-ai

sourcery-ai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds strict OIDC-based bearer authentication around the A2A HTTP transport, wires a tenant/subject-scoped PostgreSQL task store with migrations and pool management into the server, and updates configuration, tests, and documentation to support secure, durable storage in non-development environments.

Sequence diagram for authenticated A2A task handling with PostgreSQL taskstore

sequenceDiagram
    actor Client
    participant Middleware as Authenticator.Middleware
    participant Verifier as OIDCVerifier.Verify
    participant REST as a2asrv.NewRESTHandler
    participant Interceptor as identityInterceptor.Before
    participant Store as Store.Create

    Client->>Middleware: HTTP request with Authorization
    Middleware->>Verifier: Verify(ctx, rawToken)
    Verifier-->>Middleware: Identity{Issuer,Subject,Tenant,Scopes}
    Middleware->>Middleware: WithIdentity(ctx, identity)
    Middleware->>Middleware: Header.Del(Authorization, Proxy-Authorization, Cookie)
    Middleware->>REST: ServeHTTP(response, request)
    REST->>Interceptor: Before(ctx, callCtx, request)
    Interceptor->>Interceptor: requestTenant(payload)
    Interceptor->>Interceptor: setRequestTenant(payload, identity.Tenant)
    Interceptor->>REST: NewAuthenticatedUser(ownerKey(identity), attributes)
    REST->>Store: Create(ctx, task)
    Store->>Store: verifiedIdentity(ctx)
    Store->>Store: INSERT INTO a2a_tasks(... tenant_id, owner_subject ...)
    Store-->>REST: TaskVersion
    REST-->>Client: HTTP response
Loading

File-Level Changes

Change Details Files
Introduce configurable OIDC and PostgreSQL settings and validation in application configuration.
  • Extend Config with OIDCConfig and DatabaseConfig sections loaded from environment variables.
  • Validate issuer URL, algorithms, scopes, clock skew, and HTTP timeout for OIDC.
  • Validate PostgreSQL URL, disallow inline passwords, enforce absolute password file path, and parse pool tuning parameters.
  • Add unit tests covering valid and invalid OIDC and database configurations.
internal/config/config.go
internal/config/config_test.go
.env.example
Wrap the A2A server with authentication middleware and hook verifier/interceptor into the SDK handler.
  • Extend Dependencies to include Authentication and require it outside development/test.
  • Configure Agent Card security schemes from the authenticator and ensure it remains publicly readable.
  • Insert authentication call interceptor into a2asrv handler and use middleware that runs before request body limiting.
  • Add tests to ensure unauthenticated requests are rejected, authenticated requests succeed, and auth runs before 413 body limits.
internal/server/server.go
internal/server/server_test.go
docs/architecture.md
README.md
Implement OIDC verification and HTTP bearer authentication, including context propagation into A2A calls.
  • Add Authenticator type that extracts, verifies, and strips bearer credentials, writes stable auth errors, and sets Identity in context.
  • Add identity interceptor that injects authoritative tenant and authenticated user into A2A requests and rejects tenant mismatches.
  • Implement OIDCVerifier using coreos/go-oidc with strict issuer, JWKS URL, redirect, clock-skew, and claim validation.
  • Add comprehensive unit tests for bearer parsing, Agent Card configuration, OIDC claim handling, JWKS rotation, and unsafe config rejection.
internal/auth/auth.go
internal/auth/auth_test.go
internal/auth/oidc.go
internal/auth/oidc_test.go
Wire PostgreSQL task persistence into the server with a tenant/subject-scoped taskstore implementation and pool management.
  • Add PoolConfig and OpenPool helpers to build a pgx pool from DATABASE_URL and a permission-checked password file.
  • Implement Store as a taskstore.Store backed by PostgreSQL that scopes by tenant/owner, enforces global ID uniqueness, and supports CAS updates.
  • Implement List with tenant/owner filters, keyset pagination cursor encoding/decoding, and response shaping for history/artifacts.
  • Add integration tests covering CRUD, isolation, CAS vs. unconditional concurrency, list semantics, migrations, and end-to-end HTTP identity propagation.
internal/storage/postgres/store.go
internal/storage/postgres/store_integration_test.go
internal/storage/postgres/pool.go
internal/storage/postgres/pool_test.go
internal/storage/postgres/cursor.go
internal/storage/postgres/cursor_test.go
Introduce forward-only, checksummed schema migrations with advisory locking and a dedicated migrate command.
  • Embed SQL migration files and compute versioned checksums for the a2a_tasks schema.
  • Implement Migrate to apply pending migrations under a PostgreSQL advisory lock and record checksums in a ledger table.
  • Implement VerifySchema to ensure the database matches embedded migrations and is not behind.
  • Add a cmd/migrate CLI that opens a minimal pool from DATABASE_URL and runs migrations plus schema verification.
internal/storage/postgres/migrations.go
internal/storage/postgres/migrations_test.go
internal/storage/postgres/migrations/0001_create_a2a_tasks.sql
cmd/migrate/main.go
Update server startup wiring to conditionally enable OIDC and PostgreSQL based on configuration, while preserving the development-only in-memory store and Redpanda gate.
  • Refactor cmd/server main into run() that returns errors instead of exiting directly.
  • Initialize OIDC verifier and Authenticator when OIDC is enabled and enforce that DATABASE_URL requires authentication.
  • Open a PostgreSQL pool with configured limits, verify schema on startup, and inject postgres.Store as TaskStore when configured.
  • Retain in-memory task store and anonymous authenticator in development/test when no database is configured, and keep the Redpanda production dispatcher gate for later phases.
cmd/server/main.go
internal/server/server.go
internal/orchestrator/executor.go
go.mod
go.sum
docs/architecture.md
README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@KB01111, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 10 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 879ccaee-8a98-4809-980a-0359c91476c9

📥 Commits

Reviewing files that changed from the base of the PR and between 1832e3d and 2ac1a5b.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (23)
  • README.md
  • cmd/server/main.go
  • docs/architecture.md
  • go.mod
  • internal/auth/auth.go
  • internal/auth/auth_test.go
  • internal/auth/oidc.go
  • internal/auth/oidc_test.go
  • internal/config/config.go
  • internal/config/config_test.go
  • internal/server/agent_card.go
  • internal/server/agent_card_test.go
  • internal/server/error_boundary.go
  • internal/server/error_boundary_test.go
  • internal/server/server.go
  • internal/server/server_test.go
  • internal/storage/postgres/migrations.go
  • internal/storage/postgres/migrations/0001_create_a2a_tasks.sql
  • internal/storage/postgres/migrations_test.go
  • internal/storage/postgres/pool.go
  • internal/storage/postgres/pool_test.go
  • internal/storage/postgres/store.go
  • internal/storage/postgres/store_integration_test.go
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added OIDC bearer-token authentication with issuer, scope, audience, and tenant validation.
    • Added PostgreSQL-backed task storage with tenant/owner isolation, pagination, filtering, version conflict protection, and task history controls.
    • Added database migration, schema verification, connection pooling, and health-check support.
    • Production-like deployments now require authentication, while development can continue with anonymous in-memory storage.
  • Documentation

    • Added setup guidance for authentication, PostgreSQL development, migrations, and deployment storage requirements.

Walkthrough

The change adds OIDC authentication, tenant-aware request handling, PostgreSQL task persistence, migration management, configuration loading, server startup wiring, and integration tests.

Changes

Authenticated PostgreSQL runtime

Layer / File(s) Summary
OIDC and PostgreSQL configuration
.env.example, internal/config/*, go.mod
Adds OIDC and PostgreSQL settings with validation for URLs, algorithms, claims, credentials, pool limits, and durations.
OIDC verification and request identity
internal/auth/*, docs/architecture.md
Adds JWT verification, JWKS caching, bearer middleware, tenant enforcement, A2A identity propagation, and agent-card security metadata.
PostgreSQL pool and migration foundation
internal/storage/postgres/pool.go, internal/storage/postgres/migrations.go, internal/storage/postgres/migrations/*, related tests
Adds secure pool creation, embedded migrations, checksums, advisory locking, schema verification, and the initial task schema.
Tenant-scoped PostgreSQL task store
internal/storage/postgres/store.go, internal/storage/postgres/cursor.go, integration tests
Adds identity-scoped CRUD, optimistic concurrency, cursor pagination, filtering, history shaping, and isolation tests.
Server startup and authenticated storage wiring
cmd/*, internal/server/*, internal/orchestrator/*, README.md
Connects configuration, authentication, migrations, PostgreSQL storage, middleware, server lifecycle handling, and executor tenant fallback.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Authenticator
  participant OIDCVerifier
  participant Server
  participant PostgreSQL
  Client->>Authenticator: Send bearer token request
  Authenticator->>OIDCVerifier: Verify token
  OIDCVerifier-->>Authenticator: Return identity
  Authenticator->>Server: Forward request with identity context
  Server->>PostgreSQL: Read or write tenant-scoped task
  PostgreSQL-->>Server: Return task result
  Server-->>Client: Return authenticated response
Loading

Possibly related PRs

Poem

A rabbit checks the token gate,
Then stores each task with tenant state.
JWKS keys turn, migrations run,
PostgreSQL keeps the work in one.
“Hop securely!” the rabbit cries,
While scoped requests pass through the skies.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the PR's main changes: securing authentication with OIDC and adding PostgreSQL task persistence.
Description check ✅ Passed The description directly explains the OIDC authentication, PostgreSQL persistence, migration workflow, security invariants, and verification steps.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch phase/2-auth-postgres

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Aug 4, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • OIDC configuration is validated both in internal/config/loadOIDCConfig and internal/auth/validateOIDCConfig; consider consolidating this logic to a single source of truth to avoid subtle drift between the two validators.
  • The auth middleware currently strips Authorization, Proxy-Authorization, and Cookie only after successful verification; if a verifier implementation does logging or tracing on the raw token, consider moving the header scrubbing earlier or enforcing a consistent approach to avoid accidental credential exposure in future changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- OIDC configuration is validated both in `internal/config/loadOIDCConfig` and `internal/auth/validateOIDCConfig`; consider consolidating this logic to a single source of truth to avoid subtle drift between the two validators.
- The auth middleware currently strips `Authorization`, `Proxy-Authorization`, and `Cookie` only after successful verification; if a verifier implementation does logging or tracing on the raw token, consider moving the header scrubbing earlier or enforcing a consistent approach to avoid accidental credential exposure in future changes.

## Individual Comments

### Comment 1
<location path="internal/config/config.go" line_range="247-249" />
<code_context>
+	}, nil
+}
+
+func splitCSV(value string) []string {
+	var result []string
+	for item := range strings.SplitSeq(value, ",") {
+		if item = strings.TrimSpace(item); item != "" {
+			result = append(result, item)
</code_context>
<issue_to_address>
**issue (bug_risk):** splitCSV iterates over indices rather than values, and strings.SplitSeq is nonstandard, which will not compile as written.

In this loop, `item` is the index, not the slice element, so it will be of type `int`, which breaks the trimming and append logic. In addition, `strings.SplitSeq` is not a standard library function and will not compile unless you’ve defined it yourself. To use the standard library, iterate as `for _, item := range strings.Split(value, ",") { ... }`, then trim and filter non-empty items before appending to `result`.
</issue_to_address>

### Comment 2
<location path="internal/storage/postgres/store_integration_test.go" line_range="158" />
<code_context>
+	var wg sync.WaitGroup
+	errorsOut := make(chan error, contenders)
+	versions := make(chan taskstore.TaskVersion, contenders)
+	for i := range contenders {
+		wg.Add(1)
+		go func() {
</code_context>
<issue_to_address>
**issue (testing):** The CAS concurrency test loop does not iterate, so the concurrent update behavior is never exercised

In `TestPostgresStoreConcurrentVersions`, `contenders` is an `int`, so `for i := range contenders` won’t compile or run any goroutines. Update this to a standard numeric loop, e.g. `for i := 0; i < contenders; i++ {`, so the test actually exercises concurrent CAS behavior.
</issue_to_address>

### Comment 3
<location path="internal/auth/oidc.go" line_range="26" />
<code_context>
+	now            func() time.Time
+}
+
+func NewOIDCVerifier(ctx context.Context, cfg config.OIDCConfig) (*OIDCVerifier, error) {
+	issuerURL, err := validateOIDCConfig(cfg)
+	if err != nil {
</code_context>
<issue_to_address>
**issue (complexity):** Consider simplifying the OIDC verifier construction by validating configuration only once and using the concrete `*oidc.IDTokenVerifier` instead of an extra claims-verifier abstraction.

You’re re-validating and re-wrapping quite a bit, which adds indirection without much gain. Two focused changes can reduce complexity while keeping behavior intact:

### 1. Avoid double `validateOIDCConfig` calls

`validateOIDCConfig` is called in both `NewOIDCVerifier` and `newOIDCVerifier`, causing redundant work and error paths. You can validate once and pass the `issuerURL` down:

```go
func NewOIDCVerifier(ctx context.Context, cfg config.OIDCConfig) (*OIDCVerifier, error) {
    issuerURL, err := validateOIDCConfig(cfg)
    if err != nil {
        return nil, err
    }
    httpClient := &http.Client{
        Timeout:       cfg.HTTPTimeout,
        CheckRedirect: oidcRedirectPolicy(issuerURL.Scheme == "http"),
    }
    return newOIDCVerifier(ctx, cfg, issuerURL, httpClient)
}

func newOIDCVerifier(
    ctx context.Context,
    cfg config.OIDCConfig,
    issuerURL *url.URL,
    sourceClient *http.Client,
) (*OIDCVerifier, error) {
    if sourceClient == nil {
        return nil, fmt.Errorf("OIDC HTTP client is required")
    }

    httpClient := *sourceClient
    httpClient.Timeout = cfg.HTTPTimeout
    httpClient.CheckRedirect = oidcRedirectPolicy(issuerURL.Scheme == "http")

    providerContext := oidc.ClientContext(ctx, &httpClient)
    provider, err := oidc.NewProvider(providerContext, cfg.Issuer)
    if err != nil {
        return nil, fmt.Errorf("discover OIDC provider: %w", err)
    }

    // ... rest unchanged ...
}
```

If you need `newOIDCVerifier` to be usable without `NewOIDCVerifier`, you can add a small wrapper that validates when `issuerURL` is `nil`, but avoid calling `validateOIDCConfig` twice in the common path.

### 2. Inline `claimsVerifier` / `oidcClaimsVerifier`

The `claimsVerifier` interface only wraps `oidc.IDTokenVerifier.Verify` and `Claims`, and you still parse claims manually. Dropping the interface and using `*oidc.IDTokenVerifier` directly simplifies the flow:

```go
type OIDCVerifier struct {
    verifier    *oidc.IDTokenVerifier
    issuer      string
    tenantClaim string
    clockSkew   time.Duration
    now         func() time.Time
}

func newOIDCVerifier(... ) (*OIDCVerifier, error) {
    // ...
    idTokenVerifier := provider.VerifierContext(providerContext, &oidc.Config{
        ClientID:             cfg.Audience,
        SupportedSigningAlgs: append([]string(nil), cfg.AllowedAlgorithms...),
        SkipExpiryCheck:      true,
    })
    return &OIDCVerifier{
        verifier:    idTokenVerifier,
        issuer:      cfg.Issuer,
        tenantClaim: cfg.TenantClaim,
        clockSkew:   cfg.ClockSkew,
        now:         time.Now,
    }, nil
}

func (v *OIDCVerifier) Verify(ctx context.Context, rawToken string) (Identity, error) {
    claims := make(map[string]json.RawMessage)
    token, err := v.verifier.Verify(ctx, rawToken)
    if err != nil {
        return Identity{}, ErrInvalidToken
    }
    if err := token.Claims(&claims); err != nil {
        return Identity{}, ErrInvalidToken
    }

    // rest of Verify unchanged
}
```

This keeps all functionality (including custom claim parsing and skew checks) but removes an abstraction layer that doesn’t currently buy you polymorphism.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread internal/config/config.go Outdated
Comment thread internal/storage/postgres/store_integration_test.go Outdated
Comment thread internal/auth/oidc.go
@codemetrics-ai

codemetrics-ai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Note

Review skipped

Codemetrics is installed on this repository, but it hasn't been added to your team yet.

To enable AI code reviews for this repo, go to your dashboard and add A2A-RedPandaServer-Container to your team's repositories.

@qltysh

qltysh Bot commented Aug 4, 2026

Copy link
Copy Markdown

31 new issues

Tool Category Rule Count
qlty Structure Function with many returns (count = 11): run 17
qlty Structure Function with high complexity (count = 21): run 6
qlty Structure High total complexity (count = 52) 4
golangci-lint Lint QF1003: could use tagged switch on status 2
qlty Structure Complex binary expression 2

Comment thread internal/auth/oidc.go
Comment on lines +60 to +62
allowLoopbackHTTP := issuerURL.Scheme == "http"
if _, err := validateRemoteURL(metadata.JWKSURL, allowLoopbackHTTP, true); err != nil {
return nil, fmt.Errorf("unsafe OIDC jwks_uri: %w", err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The discovered jwks_uri and all redirect destinations are accepted when they are any HTTPS URL, including private or internal hosts unrelated to the configured issuer. A malicious or compromised discovery response can therefore make the server fetch JWKS data from an arbitrary internal HTTPS endpoint. Restrict JWKS retrieval and redirects with a private-network-aware dial policy and an explicit trusted-host policy appropriate for the deployment. [ssrf]

Severity Level: Critical 🚨
- ❌ OIDC startup can probe private HTTPS services.
- ⚠️ JWKS discovery is not restricted to the issuer trust boundary.
- ⚠️ Internal service responses may be exposed through observable authentication behavior.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** internal/auth/oidc.go
**Line:** 60:62
**Comment:**
	*Ssrf: The discovered `jwks_uri` and all redirect destinations are accepted when they are any HTTPS URL, including private or internal hosts unrelated to the configured issuer. A malicious or compromised discovery response can therefore make the server fetch JWKS data from an arbitrary internal HTTPS endpoint. Restrict JWKS retrieval and redirects with a private-network-aware dial policy and an explicit trusted-host policy appropriate for the deployment.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 401027f: discovery redirects and jwks_uri are restricted to the exact issuer origin, and the production dialer resolves and connects through a private, loopback, link-local, and special-use IP deny policy. Development and test retain explicit private-loopback support. Cross-origin and private-target tests were added.

Comment thread internal/config/config.go Outdated
Comment on lines +133 to +137
func loadOIDCConfig() (OIDCConfig, error) {
issuer := strings.TrimSpace(os.Getenv("OIDC_ISSUER"))
audience := strings.TrimSpace(os.Getenv("OIDC_AUDIENCE"))
if issuer == "" && audience == "" {
return OIDCConfig{}, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: When both OIDC_ISSUER and OIDC_AUDIENCE are empty, every other OIDC setting is silently ignored and authentication is disabled. Supplying OIDC_REQUIRED_SCOPES, OIDC_ALLOWED_ALGORITHMS, or OIDC_TENANT_CLAIM without the two required connection settings should instead return a configuration error; otherwise a partially configured deployment can run unauthenticated while appearing to have OIDC policy configured. [security]

Severity Level: Critical 🚨
- ❌ Incomplete OIDC deployments can run without authentication.
- ⚠️ Configured scope policy is silently ignored.
- ⚠️ Startup does not reveal the authentication misconfiguration.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** internal/config/config.go
**Line:** 133:137
**Comment:**
	*Security: When both `OIDC_ISSUER` and `OIDC_AUDIENCE` are empty, every other OIDC setting is silently ignored and authentication is disabled. Supplying `OIDC_REQUIRED_SCOPES`, `OIDC_ALLOWED_ALGORITHMS`, or `OIDC_TENANT_CLAIM` without the two required connection settings should instead return a configuration error; otherwise a partially configured deployment can run unauthenticated while appearing to have OIDC policy configured.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 401027f: any non-empty OIDC policy variable without the issuer and audience pair now fails startup instead of silently disabling authentication.

Comment thread internal/storage/postgres/migrations.go Outdated
Comment on lines +119 to +123
applied, err := loadAppliedMigrations(ctx, pool)
if err != nil {
return err
}
return verifyMigrationSet(migrations, applied, false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: VerifySchema reads the migration ledger without acquiring the migration advisory lock. A migrator can therefore apply a migration immediately after verification and while the server is starting, allowing the process to run against a schema that was not the schema it verified. Acquire the same session advisory lock for the complete verification operation, or otherwise coordinate verification and migration. [race condition]

Severity Level: Major ⚠️
- ⚠️ Server startup can race concurrent schema changes.
- ❌ An incompatible migration can break durable task operations.
- ⚠️ Verification does not coordinate with the migration command.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** internal/storage/postgres/migrations.go
**Line:** 119:123
**Comment:**
	*Race Condition: `VerifySchema` reads the migration ledger without acquiring the migration advisory lock. A migrator can therefore apply a migration immediately after verification and while the server is starting, allowing the process to run against a schema that was not the schema it verified. Acquire the same session advisory lock for the complete verification operation, or otherwise coordinate verification and migration.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Hardened in 401027f: VerifySchema now acquires the same session advisory lock and performs all ledger reads on that connection. Lock acquisition or unlock ambiguity destroys the pooled connection, and pg_advisory_unlock must return true. Deployment docs still prohibit concurrent migrations because a short startup lock cannot guard the full process lifetime.

Comment on lines +162 to +166
return &taskstore.StoredTask{
Task: task,
Version: taskstore.TaskVersion(version),
User: owner,
}, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The store persists only the raw subject as StoredTask.User, but the authentication interceptor identifies the caller with the length-prefixed ownerKey containing issuer, tenant, and subject. The A2A task-store authorization path compares the stored user with the authenticated user name, so tasks loaded from PostgreSQL can fail ownership checks for subsequent operations. Persist and return the same canonical owner value used by the interceptor, or change both implementations to use a shared identity contract. [api mismatch]

Severity Level: Major ⚠️
- ❌ Persisted task retrieval can fail ownership checks.
- ❌ Follow-up A2A task operations can be rejected.
- ⚠️ Durable tasks behave differently from in-memory task handling.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** internal/storage/postgres/store.go
**Line:** 162:166
**Comment:**
	*Api Mismatch: The store persists only the raw subject as `StoredTask.User`, but the authentication interceptor identifies the caller with the length-prefixed `ownerKey` containing issuer, tenant, and subject. The A2A task-store authorization path compares the stored user with the authenticated user name, so tasks loaded from PostgreSQL can fail ownership checks for subsequent operations. Persist and return the same canonical owner value used by the interceptor, or change both implementations to use a shared identity contract.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Already resolved on c0236b2 and retained in 401027f: PostgreSQL returns auth.OwnerKey with issuer, tenant, and subject, matching the SDK interceptor ownership contract.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 17

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/server/main.go`:
- Line 35: Update the error returned in the server configuration validation to
start with a lowercase character, changing the message in the relevant error
construction while preserving the environment value and remaining wording.

In `@go.mod`:
- Around line 12-19: Update the golang.org/x/text indirect dependency in go.mod
from v0.37.0 to v0.39.0 or later, preserving the existing dependency declaration
and ensuring the resolved module version reflects the upgrade.

In `@internal/auth/auth_test.go`:
- Line 54: Replace every httptest.NewRequest call in the auth tests with
httptest.NewRequestWithContext, passing t.Context() so requests are bound to the
test lifetime. Apply this consistently to the request setups in the affected
tests, including the calls near lines 54, 90, 113, and 133.

In `@internal/auth/auth.go`:
- Around line 240-254: Update writeAuthError to encode a typed response struct
instead of nested map[string]any, satisfying errchkjson while preserving the
existing JSON shape and checked-error handling. Set WWW-Authenticate for both
unauthorized and forbidden statuses, using invalid_token for 401 and
insufficient_scope for 403. Update the related assertion in auth_test.go to
expect the 403 challenge.

In `@internal/auth/oidc_test.go`:
- Line 167: Fix the four golangci-lint findings in the tests: update the
ErrInvalidToken assertions at the branches around lines 167 and 219 to use
errors.Is, matching the existing pattern near line 110, and update the Encode
calls around lines 338 and 365 to encode a typed value or capture and check the
returned error. Preserve the current test behavior while ensuring all errors are
handled.
- Around line 230-246: Extend
TestNewOIDCVerifierRejectsIssuerMismatchAndUnsafeJWKS with a redirect-policy
case that makes the discovery endpoint return a 302 to a cleartext non-loopback
URL, then call newOIDCVerifier and assert discovery fails. Exercise
oidcRedirectPolicy through the discovery request rather than the JWKS override,
and verify the returned error indicates provider discovery failure.
- Around line 115-122: Remove the custom stringsContain helper and import the
standard-library strings package. Update all three call sites in the affected
test code to use strings.Contains with the existing value and fragment
arguments.

In `@internal/auth/oidc.go`:
- Around line 49-70: Update NewOIDCVerifier’s oidc.VerifierContext call to use a
long-lived context independent of the startup context’s cancellation or timeout,
such as context.WithoutCancel(providerContext). Preserve the existing bounded
HTTP client and all verifier configuration, including SupportedSigningAlgs and
SkipExpiryCheck.

In `@internal/config/config_test.go`:
- Around line 146-165: Add table-driven coverage for both remaining validation
branches in loadDatabaseConfig: DATABASE_PASSWORD_FILE set without DATABASE_URL,
and a relative password-file path with a valid database URL. Reuse
setValidEnvironment, set the relevant environment variables per subtest, and
assert Load returns an error for each case.

In `@internal/config/config.go`:
- Around line 157-167: Consolidate the duplicated OIDC security decisions by
exporting one algorithm allow-list and the loopback-host helper from the config
package, then update loadOIDCConfig and validateOIDCConfig in auth to reuse
those shared symbols. Remove the local algorithm map and isLoopbackHost
implementations while preserving their current validation and loopback behavior.
- Around line 146-148: Pass the resolved environment from Load into
loadOIDCConfig, then allow the HTTP loopback exception only outside staging and
production. Keep HTTPS required for all non-loopback issuers and reject
cleartext loopback OIDC_ISSUER values when the environment is staging or
production.

In `@internal/server/server_test.go`:
- Line 237: Update the HTTP request in the agent-card test around cardResponse
to use http.NewRequestWithContext with t.Context(), then execute it via
testServer.Client().Do instead of Client().Get; preserve the existing URL and
response handling.

In `@internal/storage/postgres/migrations_test.go`:
- Around line 16-32: Add a focused test for the migration-gap branch in
verifyMigrationSet, using a synthetic two-migration slice where only the
higher-version migration is applied and migration mode is enabled; assert that
verification returns an error for the missing lower-version migration.

In `@internal/storage/postgres/migrations/0001_create_a2a_tasks.sql`:
- Around line 1-12: Make task identity tenant-scoped: in
internal/storage/postgres/migrations/0001_create_a2a_tasks.sql lines 1-12,
define task_id as NOT NULL and use a composite primary key of (tenant_id,
task_id); in internal/storage/postgres/store.go lines 55-68, update the conflict
target to (tenant_id, task_id); in
internal/storage/postgres/store_integration_test.go lines 46-48, update the test
task ID to a UUID to cover cross-tenant collisions.

In `@internal/storage/postgres/pool.go`:
- Around line 94-111: Update readPasswordFile to open the password file once,
obtain metadata from that file handle, and perform the regular-file, permission,
and size checks against it before reading. Replace the path-based os.ReadFile
call with a handle-based read (using io as needed), preserve the existing error
messages and limits, and ensure the handle is closed on every path.

In `@internal/storage/postgres/store_integration_test.go`:
- Around line 319-337: Resolve all reported golangci-lint findings in this test
file: update httpTestVerifier.Verify to avoid initializing tenant to an unused
empty string before the switch, while preserving its token-to-tenant mapping and
invalid-token return; explicitly handle the Body.Close return values at the two
reported call sites, using deferred closure cleanup for sendResponse.Body and
ignored-error assignment for response.Body.

In `@internal/storage/postgres/store.go`:
- Around line 309-312: Update the StatusTimestampAfter predicate in listFilter
to require a non-null status_timestamp strictly greater than the supplied
timestamp, replacing the current IS NULL OR >= condition with a > comparison
while preserving parameter numbering.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8d7f9951-1c28-44b1-ad68-60599015f6be

📥 Commits

Reviewing files that changed from the base of the PR and between e51d6bf and 1832e3d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (24)
  • .env.example
  • README.md
  • cmd/migrate/main.go
  • cmd/server/main.go
  • docs/architecture.md
  • go.mod
  • internal/auth/auth.go
  • internal/auth/auth_test.go
  • internal/auth/oidc.go
  • internal/auth/oidc_test.go
  • internal/config/config.go
  • internal/config/config_test.go
  • internal/orchestrator/executor.go
  • internal/server/server.go
  • internal/server/server_test.go
  • internal/storage/postgres/cursor.go
  • internal/storage/postgres/cursor_test.go
  • internal/storage/postgres/migrations.go
  • internal/storage/postgres/migrations/0001_create_a2a_tasks.sql
  • internal/storage/postgres/migrations_test.go
  • internal/storage/postgres/pool.go
  • internal/storage/postgres/pool_test.go
  • internal/storage/postgres/store.go
  • internal/storage/postgres/store_integration_test.go

Comment thread cmd/server/main.go Outdated
Comment thread go.mod Outdated
Comment thread internal/auth/auth_test.go Outdated
Comment thread internal/auth/auth.go Outdated
Comment thread internal/auth/oidc_test.go Outdated
Comment thread internal/storage/postgres/migrations_test.go
Comment thread internal/storage/postgres/migrations/0001_create_a2a_tasks.sql
Comment thread internal/storage/postgres/pool.go
Comment thread internal/storage/postgres/store_integration_test.go
Comment thread internal/storage/postgres/store.go

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@entelligence-ai-pr-reviews

entelligence-ai-pr-reviews Bot commented Aug 4, 2026

Copy link
Copy Markdown

EntelligenceAI PR Summary

This PR introduces strict OIDC bearer-token authentication with private IP blocking and a PostgreSQL-backed task store with tenant/owner isolation, optimistic concurrency, and keyset pagination. It adds config validation for both auth and database secret handling, wires authentication and database initialization into the server startup, and sanitizes internal errors before they reach clients. The PR also includes migrations, tests, and documentation updates.


Review Scorecard

Dimension Rating Basis
Code Quality ●●●○○ 3/5 — Needs Work no critical/significant findings — rated Needs Work
Blast Radius High Before is referenced in 1 other file(s), touches auth/access-control, persisted data / migrations, infra / deploy / config, 19 file(s) / ~2051 line(s) changed (size only — not a blast signal)
Merge Confidence ●●●○○ 3/5 — Review Recommended code quality 3/5 × High blast radius

Review recommended before merging. 8 unresolved comment(s) from previous reviews remain open.

Key Findings:

  • 8 unresolved comment(s) from previous reviews still need to be addressed
  • 25/28 changed files reviewed (89% coverage)
Evaluated against
  • 25/28 changed files reviewed
  • criteria: correctness, security & access control, robustness & error handling, concurrency & data integrity, repo conventions / steering docs
  • steering docs: none found in repo

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@KB01111
KB01111 merged commit 7156ab6 into main Aug 4, 2026
2 checks passed
@KB01111
KB01111 deleted the phase/2-auth-postgres branch August 4, 2026 01:05
Comment thread internal/config/config.go
if err != nil {
return DatabaseConfig{}, err
}
minConnections, err := parseInt32("DATABASE_MIN_CONNECTIONS", 2, 0, maxConnections)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

NIT CORRECTNESS Default DATABASE_MIN_CONNECTIONS (2) can exceed a validly-configured DATABASE_MAX_CONNECTIONS, causing a confusing startup failure

When an operator sets DATABASE_MAX_CONNECTIONS=1 (valid: range is 1–500) but leaves DATABASE_MIN_CONNECTIONS unset, parseInt32 falls back to the default "2" and then rejects it because 2 > maxConnections(1). The server fails to start with "DATABASE_MIN_CONNECTIONS must be between 0 and 1" — blaming a variable the operator never set. The validation is safe (no wrong runtime result), but the default min (2) is inconsistent with the allowed max floor (1).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant