Skip to content

fix(config): accept single string or array for TOML list fields#1377

Merged
kixelated merged 2 commits intomainfrom
claude/support-array-string-input-IqGvz
May 5, 2026
Merged

fix(config): accept single string or array for TOML list fields#1377
kixelated merged 2 commits intomainfrom
claude/support-array-string-input-IqGvz

Conversation

@kixelated
Copy link
Copy Markdown
Collaborator

Summary

Closes #1376.

Several TLS/auth config fields in the relay are typed as Vec<_> but were documented in the example TOMLs (e.g. demo/relay/prod.toml) as plain strings. Loading those configs fails with:

invalid type: string, expected a sequence

Apply serde_with::OneOrMany<_, PreferMany> (already used elsewhere in the codebase, e.g. ClusterConfig.connect, PublicDetailed.subscribe/publish) so a bare string and a TOML array both deserialize into a Vec.

Affected fields

  • server.tls.cert, server.tls.key, server.tls.generate, server.tls.rootmoq_native::ServerTlsConfig
  • tls.rootmoq_native::ClientTls
  • web.https.rootmoq_relay::HttpsConfig
  • auth.tls.rootmoq_relay::AuthTls
  • auth.domainsmoq_relay::AuthConfig

CLI behavior is unchanged (still value_delimiter = ',' / repeated flags). Existing array-form TOML configs continue to work.

Test plan

  • cargo check -p moq-native -p moq-relay --all-targets
  • cargo clippy -p moq-native -p moq-relay --all-targets -- -D warnings
  • cargo fmt --all --check
  • cargo test -p moq-native --lib server::tests (new test_tls_string_or_array covers both single-string and array forms)
  • cargo test -p moq-relay --lib (75 tests pass)

Generated by Claude Code

Several TLS/auth config fields are typed as Vec but were documented
in the example TOML as plain strings, causing
"invalid type: string, expected a sequence" at startup. Apply
serde_with::OneOrMany so a bare string and a TOML array both
deserialize correctly.

Affected fields: server.tls.cert, server.tls.key, server.tls.generate,
server.tls.root, client tls.root, web.https.root, auth.tls.root,
auth.domains.

Closes #1376
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f301dfbc-16fc-4186-ae25-b5b6f1ff06f1

📥 Commits

Reviewing files that changed from the base of the PR and between 09671ad and 6cec8b9.

📒 Files selected for processing (4)
  • rs/moq-native/src/client.rs
  • rs/moq-native/src/server.rs
  • rs/moq-relay/src/auth.rs
  • rs/moq-relay/src/web.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • rs/moq-native/src/server.rs
  • rs/moq-relay/src/auth.rs
  • rs/moq-relay/src/web.rs

Walkthrough

Configuration structs in four areas were updated to accept either a single string or a TOML array for certain fields by applying serde_with::OneOrMany deserialization adapters. Affected fields: ClientTls.root, ServerTlsConfig (cert, key, generate, root), AuthTls.root, AuthConfig.domains, and HttpsConfig.root. Field types remain vectors. CLI/env handling was unchanged. A test was added to verify single-string and array TOML inputs deserialize into the expected vectors. No runtime behavior or authorization logic was modified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 and concisely describes the main change: enabling TOML list fields to accept either single strings or arrays via serde configuration.
Description check ✅ Passed The description is directly related to the changeset, explaining the problem (deserialization errors with string values), the solution (applying OneOrMany serde attribute), and listing all affected fields.
Linked Issues check ✅ Passed The PR fully addresses issue #1376 by allowing TLS config fields (cert, key, generate, root) and auth fields to accept either a single string or array form, resolving the reported deserialization error.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the stated objective: applying serde_with::OneOrMany to accept string-or-array forms for specific TLS/auth config fields. Documentation and tests are also appropriately scoped.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 claude/support-array-string-input-IqGvz
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/support-array-string-input-IqGvz

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 and usage tips.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
rs/moq-relay/src/auth.rs (1)

173-183: ⚡ Quick win

Consider adding TOML round-trip tests for the new single-string deserialization paths.

AuthTls.root and AuthConfig.domains both gain single-string TOML acceptance, but unlike ServerTlsConfig in server.rs, there are no tests verifying this behaviour. A string-vs-array test analogous to test_tls_string_or_array in server.rs would confirm the OneOrMany wiring is correct for both fields.

✅ Suggested tests
#[test]
fn test_toml_auth_tls_root_string_or_array() {
    let single: AuthTls = toml::from_str(r#"root = "ca.pem""#).unwrap();
    assert_eq!(single.root, vec![PathBuf::from("ca.pem")]);

    let array: AuthTls = toml::from_str(r#"root = ["ca1.pem", "ca2.pem"]"#).unwrap();
    assert_eq!(array.root, vec![PathBuf::from("ca1.pem"), PathBuf::from("ca2.pem")]);
}

#[test]
fn test_toml_auth_domains_string_or_array() {
    let single: AuthConfig = toml::from_str(r#"domains = "cdn.moq.dev""#).unwrap();
    assert_eq!(single.domains, vec!["cdn.moq.dev"]);

    let array: AuthConfig = toml::from_str(r#"domains = ["cdn.moq.dev", "api.moq.dev"]"#).unwrap();
    assert_eq!(array.domains, vec!["cdn.moq.dev", "api.moq.dev"]);
}

Also applies to: 229-309

🤖 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 `@rs/moq-relay/src/auth.rs` around lines 173 - 183, Add TOML round-trip unit
tests that verify single-string and array deserialization for AuthTls.root and
AuthConfig.domains: create tests similar to server.rs::test_tls_string_or_array
that call toml::from_str with a single string and with an array and assert the
resulting AuthTls.root (Vec<PathBuf>) and AuthConfig.domains (Vec<String/str>)
contain the expected elements; place tests near the existing config tests so
they run with cargo test and ensure serde_as OneOrMany behavior is covered.
🤖 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 `@rs/moq-native/src/server.rs`:
- Around line 714-736: The test test_tls_string_or_array currently only covers
cert and key as bare strings; update the single-string fixture and assertions to
also include generate and root (both annotated with #[serde_as(as =
"OneOrMany")] on ServerTlsConfig) so their single-string deserialization is
exercised: add generate = "localhost" and root = "ca.pem" to the single TOML,
then assert config.generate == vec!["localhost".to_string()] and config.root ==
vec![PathBuf::from("ca.pem")], keeping the existing cert and key checks.

---

Nitpick comments:
In `@rs/moq-relay/src/auth.rs`:
- Around line 173-183: Add TOML round-trip unit tests that verify single-string
and array deserialization for AuthTls.root and AuthConfig.domains: create tests
similar to server.rs::test_tls_string_or_array that call toml::from_str with a
single string and with an array and assert the resulting AuthTls.root
(Vec<PathBuf>) and AuthConfig.domains (Vec<String/str>) contain the expected
elements; place tests near the existing config tests so they run with cargo test
and ensure serde_as OneOrMany behavior is covered.
🪄 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: b3a83ab7-73e0-4ba4-a3c0-94a3f73f1e91

📥 Commits

Reviewing files that changed from the base of the PR and between cbffd39 and 09671ad.

📒 Files selected for processing (4)
  • rs/moq-native/src/client.rs
  • rs/moq-native/src/server.rs
  • rs/moq-relay/src/auth.rs
  • rs/moq-relay/src/web.rs

Comment on lines +714 to +736
fn test_tls_string_or_array() {
// Single string should deserialize into a Vec with one entry.
let single = r#"
cert = "cert.pem"
key = "key.pem"
"#;
let config: ServerTlsConfig = toml::from_str(single).unwrap();
assert_eq!(config.cert, vec![PathBuf::from("cert.pem")]);
assert_eq!(config.key, vec![PathBuf::from("key.pem")]);

// TOML arrays should still work.
let array = r#"
cert = ["a.pem", "b.pem"]
key = ["a.key", "b.key"]
generate = ["localhost"]
root = ["ca.pem"]
"#;
let config: ServerTlsConfig = toml::from_str(array).unwrap();
assert_eq!(config.cert, vec![PathBuf::from("a.pem"), PathBuf::from("b.pem")]);
assert_eq!(config.key, vec![PathBuf::from("a.key"), PathBuf::from("b.key")]);
assert_eq!(config.generate, vec!["localhost".to_string()]);
assert_eq!(config.root, vec![PathBuf::from("ca.pem")]);
}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

generate and root single-string cases are not covered by the test.

The single fixture only sets cert and key; generate and root are never exercised as bare strings, though both have #[serde_as(as = "OneOrMany")] applied. The array path tests all four fields, but the single-string path should too.

🛠️ Proposed fix
 		let single = r#"
 			cert = "cert.pem"
 			key = "key.pem"
+			generate = "localhost"
+			root = "ca.pem"
 		"#;
 		let config: ServerTlsConfig = toml::from_str(single).unwrap();
 		assert_eq!(config.cert, vec![PathBuf::from("cert.pem")]);
 		assert_eq!(config.key, vec![PathBuf::from("key.pem")]);
+		assert_eq!(config.generate, vec!["localhost".to_string()]);
+		assert_eq!(config.root, vec![PathBuf::from("ca.pem")]);
🤖 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 `@rs/moq-native/src/server.rs` around lines 714 - 736, The test
test_tls_string_or_array currently only covers cert and key as bare strings;
update the single-string fixture and assertions to also include generate and
root (both annotated with #[serde_as(as = "OneOrMany")] on ServerTlsConfig) so
their single-string deserialization is exercised: add generate = "localhost" and
root = "ca.pem" to the single TOML, then assert config.generate ==
vec!["localhost".to_string()] and config.root == vec![PathBuf::from("ca.pem")],
keeping the existing cert and key checks.

This matches the example TOML convention where cert/key/etc. are written
as plain strings, and round-trips a single-element Vec back to a string
rather than a one-element array.
@kixelated kixelated merged commit 08a79e7 into main May 5, 2026
1 check passed
@kixelated kixelated deleted the claude/support-array-string-input-IqGvz branch May 5, 2026 17:17
@moq-bot moq-bot Bot mentioned this pull request May 5, 2026
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.

Public relay cert config requires to be a vect rather than string but for one only.

2 participants