Skip to content

feat(guardrail): add config storage#103

Merged
bzp2010 merged 2 commits into
mainfrom
bzp/feat-guardrail-storage
May 12, 2026
Merged

feat(guardrail): add config storage#103
bzp2010 merged 2 commits into
mainfrom
bzp/feat-guardrail-storage

Conversation

@bzp2010
Copy link
Copy Markdown
Collaborator

@bzp2010 bzp2010 commented May 12, 2026

Summary by CodeRabbit

  • New Features

    • Introduced guardrails management system with configuration validation powered by JSON schema.
    • Added support for Bedrock guardrails integration.
    • Models now support association with guardrails via configurable guardrail IDs.
  • Refactor

    • Streamlined guardrail runtime configuration handling.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

📝 Walkthrough

Walkthrough

Refactored BedrockGuardrailRuntime to remove stored credentials and accept explicit config parameters, introduced guardrails configuration with schema-backed validation and resource storage, integrated guardrails into the resource registry, linked guardrails to models via ID references, and exported the public API.

Changes

Guardrails system introduction

Layer / File(s) Summary
Bedrock guardrail config and runtime refactoring
crates/aisix-guardrail/src/guardrails/bedrock.rs, crates/aisix-guardrail/src/guardrails/mod.rs
BedrockGuardrailConfig uses custom Debug impl to redact credentials. BedrockGuardrailRuntime now stores only an HTTP client; new(), with_client(), build_apply_url(), and sign_request() now accept explicit &BedrockGuardrailConfig parameters instead of stored runtime config. BedrockRuntimeConfig type is removed and no longer re-exported.
Bedrock API test coverage
crates/aisix-guardrail/src/guardrails/bedrock.rs
Tests updated to reflect config-driven signatures: shared config() and runtime() helpers established, debug redaction, URL building, and request signing all validate new explicit-config patterns.
Guardrails schema and entity types
src/config/entities/guardrails-schema.json, src/config/entities/guardrails.rs
JSON schema defines guardrails structure with bedrock-specific validation (required credentials/region, optional endpoint/session_token, no additional properties). GuardrailConfig enum with Bedrock variant, Guardrail entity with name and flattened config, schema loader and SCHEMA_VALIDATOR for runtime validation, and validate() function for JSON schema evaluation.
Guardrails store and validation tests
src/config/entities/guardrails.rs
GuardrailsStore wraps EntityStore<Guardrail> with /guardrails/ prefix and schema validation callback. Tests cover schema meta-structure, validation error cases (missing fields, invalid properties), type preservation in deserialization, and async store behavior with snapshot loading and watch-driven updates.
Resource registry integration
src/config/entities/mod.rs
guardrails module added and Guardrail re-exported. ResourceRegistry gains guardrails: GuardrailsStore field and initializes it in new() alongside existing stores.
Model integration with guardrails references
src/config/entities/models-schema.json, src/config/entities/models.rs
Model struct adds guardrail_ids: Vec<String> field (serde defaulted, omitted when empty). Schema updated with optional guardrail_ids array property (non-empty strings). Tests validate valid arrays, reject non-array types, and reject non-string elements; deserialization test confirms round-trip preservation.
Public API module export
src/lib.rs
New guardrail module re-exports all items from aisix_guardrail crate, making Bedrock guardrail types and configuration publicly accessible.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant BedrockGuardrailRuntime
  participant BedrockGuardrailConfig
  Client->>BedrockGuardrailRuntime: check(request, config)
  BedrockGuardrailRuntime->>BedrockGuardrailConfig: build_apply_url(config)
  BedrockGuardrailConfig-->>BedrockGuardrailRuntime: url with region/endpoint
  BedrockGuardrailRuntime->>BedrockGuardrailConfig: sign_request(config)
  BedrockGuardrailConfig-->>BedrockGuardrailRuntime: signed request
  BedrockGuardrailRuntime->>Client: response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • api7/aisix#100: Main PR updates BedrockGuardrailRuntime and its check method to accept a config parameter to match and implement the GuardrailRuntime trait.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Security Check ❌ Error Credentials in BedrockGuardrailConfig exposed when serialized. Debug redacts but Serialize does not. Serialized plaintext to etcd/API. No secret manager reference support. Add custom Serialize for BedrockGuardrailConfig, GuardrailConfig, and Guardrail to redact secrets. Support $secret:// references in schema. Integrate secret resolver for external credential management.
E2e Test Quality Review ⚠️ Warning Missing E2E tests. Only unit tests for individual components (schema, store, models). No test validating Model→guardrail_ids→GuardrailsStore→GuardrailRuntime integration. Add E2E test covering: (1) ResourceRegistry with both ModelsStore+GuardrailsStore, (2) Model.guardrail_ids references actual Guardrail entries, (3) Missing guardrails and empty array scenarios.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(guardrail): add config storage' accurately describes the main changes: introduction of guardrail configuration storage infrastructure including new schema, validation, and store implementation.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bzp/feat-guardrail-storage

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/aisix-guardrail/src/guardrails/bedrock.rs (1)

1-20: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Organize imports according to Rust conventions.

Imports should be sorted alphabetically with stdlib first, then external crates, then local modules. As per coding guidelines, Rust imports should follow: standard library first, then external crates (alphabetical), then local modules.

♻️ Proposed fix
-use std::{fmt, time::SystemTime};
-
-use async_trait::async_trait;
-use aws_credential_types::Credentials;
-use aws_sigv4::{
-    http_request::{SignableBody, SignableRequest, SigningSettings, sign},
-    sign::v4,
-};
-use aws_smithy_runtime_api::client::identity::Identity;
-use http::{HeaderMap, HeaderValue, Method, Request, StatusCode, header::CONTENT_TYPE};
-use percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
-use reqwest::{Client, Url};
-use serde::{Deserialize, Serialize};
-use thiserror::Error;
-use utoipa::ToSchema;
-
-use crate::traits::{
-    GuardrailCheckPayload, GuardrailMessage, GuardrailMessageContent, GuardrailMeta,
-    GuardrailOutcome, GuardrailRuntime, GuardrailStage,
-};
+use std::{fmt, time::SystemTime};
+
+use async_trait::async_trait;
+use aws_credential_types::Credentials;
+use aws_sigv4::{
+    http_request::{sign, SignableBody, SignableRequest, SigningSettings},
+    sign::v4,
+};
+use aws_smithy_runtime_api::client::identity::Identity;
+use http::{header::CONTENT_TYPE, HeaderMap, HeaderValue, Method, Request, StatusCode};
+use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
+use reqwest::{Client, Url};
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+use utoipa::ToSchema;
+
+use crate::traits::{
+    GuardrailCheckPayload, GuardrailMessage, GuardrailMessageContent, GuardrailMeta,
+    GuardrailOutcome, GuardrailRuntime, GuardrailStage,
+};
🤖 Prompt for 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.

In `@crates/aisix-guardrail/src/guardrails/bedrock.rs` around lines 1 - 20, The
import block at the top is not ordered per project convention; reorder the use
statements so std library imports come first (e.g., use std::{fmt,
time::SystemTime};), then external crates in alphabetical order (async_trait,
aws_credential_types, aws_sigv4, aws_smithy_runtime_api, http, percent_encoding,
reqwest, serde, thiserror, utoipa), and finally local crate imports
(crate::traits::{GuardrailCheckPayload, GuardrailMessage,
GuardrailMessageContent, GuardrailMeta, GuardrailOutcome, GuardrailRuntime,
GuardrailStage}); separate each group with a single blank line to match style
guidelines.
🧹 Nitpick comments (5)
src/config/entities/guardrails.rs (3)

38-45: ⚡ Quick win

Add documentation for the public Guardrail struct.

The public struct lacks a doc comment explaining the guardrail entity structure. As per coding guidelines, use /// for doc comments on public items in Rust.

📝 Proposed addition
+/// A guardrail entity combining metadata and runtime configuration.
+///
+/// The `guardrail` field is flattened, so `type` and `config` appear at the top level in JSON.
 #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
 pub struct Guardrail {
+    /// Human-readable name of the guardrail.
     pub name: String,

     #[serde(flatten)]
     #[schema(inline)]
     pub guardrail: GuardrailConfig,
 }
🤖 Prompt for 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.

In `@src/config/entities/guardrails.rs` around lines 38 - 45, Add a Rust doc
comment (using ///) above the public Guardrail struct describing its purpose and
fields: explain that Guardrail represents a named guardrail entity with a
human-readable name and an embedded GuardrailConfig (flattened into the struct)
that contains the actual configuration options; mention
serialization/deserialization behavior if relevant and that the config is
inlined via serde(flatten) and schema(inline). Place the comment immediately
above the pub struct Guardrail declaration to follow crate documentation
standards.

68-88: ⚡ Quick win

Add documentation and tracing to public GuardrailsStore methods.

Public methods lack doc comments and #[fastrace::trace] decorators. As per coding guidelines, use /// for doc comments on public items and #[fastrace::trace] decorator for distributed tracing on public functions in src/**/*.rs.

📝 Proposed additions
+/// Store for guardrail configuration entities.
+///
+/// Backed by an `EntityStore` with JSON Schema validation.
 #[derive(Clone)]
 pub struct GuardrailsStore {
     store: EntityStore<Guardrail>,
 }

 impl GuardrailsStore {
+    /// Creates a new guardrails store, loading existing entries from the provider.
+    #[fastrace::trace]
     pub async fn new(provider: Arc<dyn ConfigProvider + Send + Sync>) -> Self {
         Self {
             store: EntityStore::new(provider, "/guardrails/", "guardrails", Some(validate), &[])
                 .await,
         }
     }

+    /// Returns a snapshot of all guardrails indexed by ID.
+    #[fastrace::trace]
     pub fn list(&self) -> Arc<HashMap<String, ResourceEntry<Guardrail>>> {
         self.store.list()
     }

+    /// Retrieves a guardrail by its ID.
+    #[fastrace::trace]
     pub fn get_by_id(&self, id: &str) -> Option<ResourceEntry<Guardrail>> {
         self.store.get(id)
     }
 }
🤖 Prompt for 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.

In `@src/config/entities/guardrails.rs` around lines 68 - 88, Add API docs and
tracing to the public GuardrailsStore API: add /// doc comments describing the
GuardrailsStore struct and each public method (new, list, get_by_id) and
annotate the public functions with #[fastrace::trace] (i.e., #[fastrace::trace]
pub async fn new(...), #[fastrace::trace] pub fn list(&self) -> ...,
#[fastrace::trace] pub fn get_by_id(&self, id: &str) -> ...). Ensure the doc
comments briefly explain purpose/behavior and add any required fastrace import
or feature gate if missing so the attribute compiles.

23-28: ⚡ Quick win

Add documentation for the public GuardrailConfig enum.

The public enum lacks a doc comment explaining its purpose and variants. As per coding guidelines, use /// for doc comments on public items in Rust.

📝 Proposed addition
+/// Configuration for a guardrail runtime.
+///
+/// This enum is tagged by `type` and carries provider-specific configuration.
 #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
 #[serde(tag = "type", content = "config")]
 pub enum GuardrailConfig {
+    /// AWS Bedrock guardrail configuration.
     #[serde(rename = "bedrock")]
     Bedrock(BedrockGuardrailConfig),
 }
🤖 Prompt for 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.

In `@src/config/entities/guardrails.rs` around lines 23 - 28, The public enum
GuardrailConfig lacks a doc comment; add a Rust doc comment (///) above the
GuardrailConfig definition describing its purpose (a serializable enum selecting
the guardrail backend/config) and briefly document each variant, e.g., that
GuardrailConfig::Bedrock wraps BedrockGuardrailConfig for Bedrock-specific
settings; update the comment to mention serialization via serde (tag="type") if
helpful so readers know how to construct/select variants.
crates/aisix-guardrail/src/guardrails/bedrock.rs (1)

96-105: ⚡ Quick win

Add documentation comments to public functions.

Public functions new() and with_client() lack doc comments. As per coding guidelines, use /// for doc comments on public items in Rust.

📝 Proposed addition
 impl BedrockGuardrailRuntime {
+    /// Creates a new Bedrock guardrail runtime with a default HTTP client.
     pub fn new() -> Self {
         Self {
             client: Client::new(),
         }
     }

+    /// Creates a new Bedrock guardrail runtime with the provided HTTP client.
     pub fn with_client(client: Client) -> Self {
         Self { client }
     }
🤖 Prompt for 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.

In `@crates/aisix-guardrail/src/guardrails/bedrock.rs` around lines 96 - 105, Add
Rust doc comments (///) to the public constructors on BedrockGuardrailRuntime:
document pub fn new() and pub fn with_client(client: Client) to explain what
each does, their return value (a new BedrockGuardrailRuntime), and for
with_client, describe the client parameter and ownership expectations; place the
comments immediately above the respective methods in the impl block for
BedrockGuardrailRuntime.
src/config/entities/models.rs (1)

31-35: 💤 Low value

Consider tracking the temporary nature of guardrail_ids.

The comment indicates guardrail_ids is a temporary binding surface until policy evaluation handles guardrails dynamically. To prevent this temporary solution from becoming permanent, consider creating a tracking issue for the future migration to dynamic policy-based guardrail attachment.

🤖 Prompt for 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.

In `@src/config/entities/models.rs` around lines 31 - 35, Add a short-lived
tracking artifact and explicit TODO to ensure guardrail_ids on the Model struct
is not left permanent: open a tracking issue in the project tracker describing
the migration to dynamic policy-based guardrail attachment, note the issue
number, then update the comment above the pub guardrail_ids: Vec<String> field
to include a TODO/FIXME with that issue number, an expected removal milestone,
and a brief migration note so future maintainers can find the plan and timeline.
🤖 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 `@src/config/entities/guardrails-schema.json`:
- Around line 34-42: The JSON schema currently requires plaintext
"access_key_id" and "secret_access_key" (and similarly "session_token" if used);
change the schema for these properties so they can accept secret-manager
references (e.g., a string like "$secret://...") or an explicit secret reference
object rather than only a raw credential. Concretely, update the "properties"
entries for "access_key_id", "secret_access_key" (and "session_token" if
present) to allow either a non-empty string OR a secret reference (e.g., a
string matching a "^\\$secret://.+$" pattern or a union with an object form like
{"secret_ref":"<string>"}), and adjust any "minLength" constraints accordingly;
ensure the schema still lists these names in the required array only if truly
required, and add schema documentation so runtime code that reads these fields
(credential resolution logic) can detect and resolve "$secret://" references
before use.

---

Outside diff comments:
In `@crates/aisix-guardrail/src/guardrails/bedrock.rs`:
- Around line 1-20: The import block at the top is not ordered per project
convention; reorder the use statements so std library imports come first (e.g.,
use std::{fmt, time::SystemTime};), then external crates in alphabetical order
(async_trait, aws_credential_types, aws_sigv4, aws_smithy_runtime_api, http,
percent_encoding, reqwest, serde, thiserror, utoipa), and finally local crate
imports (crate::traits::{GuardrailCheckPayload, GuardrailMessage,
GuardrailMessageContent, GuardrailMeta, GuardrailOutcome, GuardrailRuntime,
GuardrailStage}); separate each group with a single blank line to match style
guidelines.

---

Nitpick comments:
In `@crates/aisix-guardrail/src/guardrails/bedrock.rs`:
- Around line 96-105: Add Rust doc comments (///) to the public constructors on
BedrockGuardrailRuntime: document pub fn new() and pub fn with_client(client:
Client) to explain what each does, their return value (a new
BedrockGuardrailRuntime), and for with_client, describe the client parameter and
ownership expectations; place the comments immediately above the respective
methods in the impl block for BedrockGuardrailRuntime.

In `@src/config/entities/guardrails.rs`:
- Around line 38-45: Add a Rust doc comment (using ///) above the public
Guardrail struct describing its purpose and fields: explain that Guardrail
represents a named guardrail entity with a human-readable name and an embedded
GuardrailConfig (flattened into the struct) that contains the actual
configuration options; mention serialization/deserialization behavior if
relevant and that the config is inlined via serde(flatten) and schema(inline).
Place the comment immediately above the pub struct Guardrail declaration to
follow crate documentation standards.
- Around line 68-88: Add API docs and tracing to the public GuardrailsStore API:
add /// doc comments describing the GuardrailsStore struct and each public
method (new, list, get_by_id) and annotate the public functions with
#[fastrace::trace] (i.e., #[fastrace::trace] pub async fn new(...),
#[fastrace::trace] pub fn list(&self) -> ..., #[fastrace::trace] pub fn
get_by_id(&self, id: &str) -> ...). Ensure the doc comments briefly explain
purpose/behavior and add any required fastrace import or feature gate if missing
so the attribute compiles.
- Around line 23-28: The public enum GuardrailConfig lacks a doc comment; add a
Rust doc comment (///) above the GuardrailConfig definition describing its
purpose (a serializable enum selecting the guardrail backend/config) and briefly
document each variant, e.g., that GuardrailConfig::Bedrock wraps
BedrockGuardrailConfig for Bedrock-specific settings; update the comment to
mention serialization via serde (tag="type") if helpful so readers know how to
construct/select variants.

In `@src/config/entities/models.rs`:
- Around line 31-35: Add a short-lived tracking artifact and explicit TODO to
ensure guardrail_ids on the Model struct is not left permanent: open a tracking
issue in the project tracker describing the migration to dynamic policy-based
guardrail attachment, note the issue number, then update the comment above the
pub guardrail_ids: Vec<String> field to include a TODO/FIXME with that issue
number, an expected removal milestone, and a brief migration note so future
maintainers can find the plan and timeline.
🪄 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: CHILL

Plan: Pro

Run ID: 3ab9a7f7-022f-4143-9ab2-41cd3159a513

📥 Commits

Reviewing files that changed from the base of the PR and between 70e5f1a and e340313.

📒 Files selected for processing (8)
  • crates/aisix-guardrail/src/guardrails/bedrock.rs
  • crates/aisix-guardrail/src/guardrails/mod.rs
  • src/config/entities/guardrails-schema.json
  • src/config/entities/guardrails.rs
  • src/config/entities/mod.rs
  • src/config/entities/models-schema.json
  • src/config/entities/models.rs
  • src/lib.rs

Comment on lines +34 to +42
"access_key_id",
"secret_access_key"
],
"properties": {
"identifier": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 },
"region": { "type": "string", "minLength": 1 },
"access_key_id": { "type": "string", "minLength": 1 },
"secret_access_key": { "type": "string", "minLength": 1 },
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 12, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Security concern: Credentials stored in plain text without secret manager support.

The schema requires access_key_id, secret_access_key, and optional session_token as plain strings, which means AWS credentials will be stored in clear text in the configuration backend (etcd). This creates significant security risks:

  • Credentials are exposed in configuration snapshots, logs, and backups
  • No rotation mechanism without config updates
  • Violates least-privilege principle for config access

Consider supporting secret manager references (e.g., $secret://path/to/credential) in these fields so credentials can be resolved at runtime from a secure vault. As per coding guidelines, environment variable and secret manager references in configuration fields must be properly resolved before use, implying the schema should allow such references.

🤖 Prompt for 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.

In `@src/config/entities/guardrails-schema.json` around lines 34 - 42, The JSON
schema currently requires plaintext "access_key_id" and "secret_access_key" (and
similarly "session_token" if used); change the schema for these properties so
they can accept secret-manager references (e.g., a string like "$secret://...")
or an explicit secret reference object rather than only a raw credential.
Concretely, update the "properties" entries for "access_key_id",
"secret_access_key" (and "session_token" if present) to allow either a non-empty
string OR a secret reference (e.g., a string matching a "^\\$secret://.+$"
pattern or a union with an object form like {"secret_ref":"<string>"}), and
adjust any "minLength" constraints accordingly; ensure the schema still lists
these names in the required array only if truly required, and add schema
documentation so runtime code that reads these fields (credential resolution
logic) can detect and resolve "$secret://" references before use.

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.

Link to #58

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

gh issue view 58 --repo api7/aisix

Length of output: 1384


@bzp2010, thanks for the reference! The security concern raised here — storing AWS credentials as plain strings in the config backend — is already tracked in issue #58 ("feat: support secret references and secret provider integrations for provider configs"), which proposes support for $env:// and $secret:// reference syntax for runtime credential resolution. I'll note this as a known, tracked limitation for now.


✏️ Learnings added
Learnt from: bzp2010
Repo: api7/aisix PR: 103
File: src/config/entities/guardrails-schema.json:34-42
Timestamp: 2026-05-12T09:15:42.333Z
Learning: In the api7/aisix repository, plaintext credential storage for sensitive fields (e.g., `access_key_id`, `secret_access_key`, `api_key`) in provider/guardrail config schemas is a known, accepted limitation tracked in GitHub issue `#58`. The planned solution is to support `$env://` and `$secret://` reference syntax for runtime credential resolution via secret provider integrations. Do not raise this as a blocking concern in reviews; instead, reference issue `#58`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@bzp2010 bzp2010 merged commit 8da3346 into main May 12, 2026
3 checks passed
@bzp2010 bzp2010 deleted the bzp/feat-guardrail-storage branch May 12, 2026 09:17
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.

1 participant