Skip to content

feat(azure-ac): add Azure App Configuration provider - #659

Merged
jdx merged 5 commits into
jdx:mainfrom
jmoreno11:feat/azure-app-configuration
Jul 29, 2026
Merged

feat(azure-ac): add Azure App Configuration provider#659
jdx merged 5 commits into
jdx:mainfrom
jmoreno11:feat/azure-app-configuration

Conversation

@jmoreno11

@jmoreno11 jmoreno11 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Adds a read-only azure-ac provider for Azure App Configuration. Discussed in #658.

Azure splits its config surface in two: Key Vault for secrets, which azure-sm already covers, and App Configuration for everything that is not a secret — endpoints, feature toggles, tuning values. Without the second half, those values get hardcoded in fnox.toml or exported from a shell script sitting next to it, and fnox.toml stops being the one place to look.

RemoteRead only, so nothing writes back. Optional label and prefix. A key in App Configuration can carry several values distinguished by a label, which maps onto profiles:

[profiles.dev.providers]
appconfig = { type = "azure-ac", endpoint = "https://myapp-config.azconfig.io", label = "dev" }

[profiles.prod.providers]
appconfig = { type = "azure-ac", endpoint = "https://myapp-config.azconfig.io", label = "prod" }

Omitting label returns the key-value that has no label.

Notes

No new dependencies — azure_identity and azure_core are already in the tree for azure-sm. Auth is DeveloperToolsCredential, same as azure-sm.

The scope constant is https://appconfig.azure.com/.default. Azure's own TypeSpec still says azconfig.io, but the Go, Python, .NET, Java and JS SDKs all use appconfig.azure.com, and the live test below confirms the SDKs are right.

fnox set against a RemoteRead provider writes the plaintext into fnox.toml rather than erroring (src/commands/set.rs:141-183). That is pre-existing behaviour shared with passwordstate, so I documented it in the provider page rather than change it here.

Verification

cargo test --workspace 249 passed, cargo clippy --all-targets -D warnings clean, cargo fmt clean, bats test/provider_add.bats 6/6, mise run render idempotent. All at MSRV 1.91.1.

Also ran against a real store, since a few things could not be confirmed from the docs. Flat keys, hierarchical keys (myapp/api-url, confirming / percent-encodes correctly in the path), the same key reached via prefix, and label vs no-label all resolved. The 404, 403 and connection-failure paths each produced the right error variant. The store has been deleted.

Summary by CodeRabbit

  • New Features
    • Added Azure App Configuration (azure-ac) as a read-only remote configuration provider.
    • Enabled fnox provider add for azure-ac, including starter template generation.
    • Added stricter endpoint validation and enhanced connection checks; configuration schema now requires endpoint with optional label/prefix.
  • Documentation
    • Documented azure-ac and linked it in provider listings/navigation and CLI docs.
  • Tests
    • Expanded provider-add coverage for azure-ac, plus added unit test coverage for connection validation and endpoint handling.

Reads key-values from an Azure App Configuration store, the Azure home
for non-secret configuration. RemoteRead only; optional label and prefix.

Closes jdx#658
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 31ad92e6-7ca5-4e52-bf0c-c4211fbeb19e

📥 Commits

Reviewing files that changed from the base of the PR and between 41e08e9 and fdd238a.

📒 Files selected for processing (1)
  • crates/fnox-core/src/providers/azure_ac.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/fnox-core/src/providers/azure_ac.rs

📝 Walkthrough

Walkthrough

Adds Azure App Configuration as a read-only remote provider with authenticated key retrieval, CLI support, schema updates, provider-add tests, and documentation.

Changes

Azure App Configuration provider

Layer / File(s) Summary
Provider configuration and remote reads
crates/fnox-core/providers/azure-ac.toml, crates/fnox-core/src/providers/azure_ac.rs
Defines provider metadata, endpoint/label/prefix handling, Azure authentication, key retrieval, error mapping, connection testing, and unit tests.
Provider module registration
crates/fnox-core/src/providers/mod.rs
Registers and instantiates the new provider module.
CLI provider type and template
src/commands/provider/mod.rs, src/commands/provider/add.rs, docs/public/schema.json, docs/cli/commands.json, fnox.usage.kdl, test/provider_add.bats
Adds the azure-ac CLI type, default configuration generation, schema support, command specifications, and provider-add test coverage.
Provider documentation and listings
docs/providers/azure-ac.md, README.md, docs/index.md, docs/providers/overview.md, docs/.vitepress/config.mjs, docs/cli/provider/add.md
Documents setup and configuration, and adds Azure App Configuration to provider listings and navigation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Fnox
  participant DeveloperToolsCredential
  participant AzureAppConfiguration
  Fnox->>DeveloperToolsCredential: Request Entra ID token
  DeveloperToolsCredential-->>Fnox: Return access token
  Fnox->>AzureAppConfiguration: GET labeled /kv/{key}
  AzureAppConfiguration-->>Fnox: Return key/value payload or HTTP error
Loading

Poem

A rabbit found a cloud-kept key,
And fetched it swiftly, bright and free.
With labels neat and prefixes light,
Azure joined the provider flight.
“Hop, hop!” the new config sings—
A tidy trail of useful things.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding the Azure App Configuration provider.
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.

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.

@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown

Greptile Summary

Adds the read-only Azure App Configuration provider and addresses the previously reported endpoint, label, and connection-validation issues.

  • Validates HTTPS App Configuration domains and selects the corresponding Azure cloud token scope.
  • Fetches labeled or prefixed key-values and maps HTTP and payload failures to provider errors.
  • Registers azure-ac across provider configuration, CLI generation, schema, documentation, and tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
crates/fnox-core/src/providers/azure_ac.rs Implements Azure App Configuration reads with endpoint allowlisting, cloud-specific scopes, status handling, and payload validation; the previously reported issues are resolved.
crates/fnox-core/providers/azure-ac.toml Defines the generated provider metadata and required endpoint plus optional label and prefix fields.
crates/fnox-core/src/providers/mod.rs Registers the new provider module with generated provider instantiation.
src/commands/provider/add.rs Adds a valid default Azure App Configuration provider template.
src/commands/provider/mod.rs Exposes azure-ac through the provider-add CLI type enumeration.
docs/public/schema.json Adds the generated schema variant for azure-ac configuration.
test/provider_add.bats Extends provider-add coverage to include the azure-ac alias and generated configuration.

Reviews (5): Last reviewed commit: "fix(azure-ac): require a key-value list ..." | Re-trigger Greptile

Comment thread crates/fnox-core/src/providers/azure_ac.rs Outdated
Comment thread crates/fnox-core/src/providers/azure_ac.rs
// An empty label is a distinct label in App Configuration, not the absence of one.
Ok(Self {
endpoint: endpoint.trim_end_matches('/').to_string(),
label: label.filter(|l| !l.is_empty()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Empty label loses its selector

When fnox.toml explicitly sets label = "", configuration resolution preserves that value but this filter converts it to None, causing the request to omit the label parameter and retrieve the unlabeled key-value instead of the distinct empty-label value.

Suggested change
label: label.filter(|l| !l.is_empty()),
label,

Knowledge Base Used: Provider system

@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: 3

🤖 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 `@crates/fnox-core/src/providers/azure_ac.rs`:
- Around line 162-171: The test_connection method currently ignores the 404
status that get intentionally passes through. Update test_connection to inspect
the response status and return an error for any non-success response, while
preserving successful connection behavior and the existing filtered /kv request.
- Around line 28-34: Validate the endpoint in AzureAppConfiguration provider
constructor new before storing or using it, requiring HTTPS and an approved
Azure App Configuration host suffix, including supported sovereign-cloud
domains. Reject invalid or untrusted endpoints with an error, while preserving
the existing trailing-slash normalization and label/prefix handling for valid
endpoints.
- Around line 7-10: Update the SCOPE constant in the Azure App Configuration
provider to use the data-plane resource scope https://azconfig.io/.default
instead of the current management-plane scope, while leaving PROVIDER_NAME,
PROVIDER_URL, and API_VERSION unchanged.
🪄 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: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e109e1fa-179f-4651-b769-b4f9e8cf8b45

📥 Commits

Reviewing files that changed from the base of the PR and between 28042e5 and eb2e08b.

📒 Files selected for processing (15)
  • README.md
  • crates/fnox-core/providers/azure-ac.toml
  • crates/fnox-core/src/providers/azure_ac.rs
  • crates/fnox-core/src/providers/mod.rs
  • docs/.vitepress/config.mjs
  • docs/cli/commands.json
  • docs/cli/provider/add.md
  • docs/index.md
  • docs/providers/azure-ac.md
  • docs/providers/overview.md
  • docs/public/schema.json
  • fnox.usage.kdl
  • src/commands/provider/add.rs
  • src/commands/provider/mod.rs
  • test/provider_add.bats

Comment on lines +7 to +10
const PROVIDER_NAME: &str = "Azure App Configuration";
const PROVIDER_URL: &str = "https://fnox.jdx.dev/providers/azure-ac";
const SCOPE: &str = "https://appconfig.azure.com/.default";
const API_VERSION: &str = "2023-11-01";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'const SCOPE' crates/fnox-core/src/providers/azure_ac.rs
curl -fsSL 'https://learn.microsoft.com/en-us/rest/api/data-plane/appconfiguration/get-key-values/get-key-values?view=rest-data-plane-appconfiguration-2023-11-01' |
  rg -F 'https://azconfig.io/.default'

Repository: jdx/fnox

Length of output: 1132


Use the App Configuration data-plane scope.

Azure App Configuration uses https://azconfig.io/.default; the current https://appconfig.azure.com/.default scope is for a different resource and won’t authorize data-plane requests.

Proposed fix
-const SCOPE: &str = "https://appconfig.azure.com/.default";
+const SCOPE: &str = "https://azconfig.io/.default";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const PROVIDER_NAME: &str = "Azure App Configuration";
const PROVIDER_URL: &str = "https://fnox.jdx.dev/providers/azure-ac";
const SCOPE: &str = "https://appconfig.azure.com/.default";
const API_VERSION: &str = "2023-11-01";
const PROVIDER_NAME: &str = "Azure App Configuration";
const PROVIDER_URL: &str = "https://fnox.jdx.dev/providers/azure-ac";
const SCOPE: &str = "https://azconfig.io/.default";
const API_VERSION: &str = "2023-11-01";
🤖 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/fnox-core/src/providers/azure_ac.rs` around lines 7 - 10, Update the
SCOPE constant in the Azure App Configuration provider to use the data-plane
resource scope https://azconfig.io/.default instead of the current
management-plane scope, while leaving PROVIDER_NAME, PROVIDER_URL, and
API_VERSION unchanged.

Comment thread crates/fnox-core/src/providers/azure_ac.rs
Comment thread crates/fnox-core/src/providers/azure_ac.rs Outdated
Comment thread crates/fnox-core/src/providers/azure_ac.rs
@jdx
jdx enabled auto-merge (squash) July 28, 2026 03:48
@jdx
jdx disabled auto-merge July 28, 2026 03:49
The endpoint decides where fnox sends an Entra token, so an auto-loaded
fnox.toml could point it at an arbitrary host. Parse it, require https,
match the parsed host against the App Configuration domains, and derive
the audience from that host.
@github-actions

Copy link
Copy Markdown
Contributor

This PR currently has failing checks. If this continues for 7 days, it will be closed automatically.

This is warning day 1 of 7.

Please update the PR when you have a chance. Feel free to reopen or create a new PR if it is closed and you'd like to continue working on it.

This comment was generated by an automated workflow.

@jmoreno11

Copy link
Copy Markdown
Contributor Author

Review rounds are addressed — 62ae3fa (the connection test swallowing a 404) and 41e08e9 (endpoint validated before the token is acquired). I've folded my earlier comments into this one and deleted them, since they were superseded and the thread was getting long.

On the perf gate: it's reporting a real regression, not noise. Measured on x86 runners, both binaries built first and then measured interleaved, 30 runs per side:

build fnox schema I refs vs base
base 28042e5 6,821,180
base minus the doppler provider 6,756,987 −0.95%
base + azure-ac (41e08e9) 7,023,682 +2.97%
base + azure-ac, module body stubbed out 7,019,631 +2.91%
doppler swapped for azure-ac, provider count held fixed 6,978,267 +2.30%

One existing provider costs ~0.95% and azure-ac costs roughly 3× that, but deleting 277 of its 310 lines moves it 0.06% — so the cost is the config variant, not the provider code. There's no new dependency (the diff touches no Cargo file), and the schema fragment is 6 fields with 1 required, which is unremarkable next to azure-kms or gcp-kms. On arm64 both azure-ac and doppler measure ~1%, so the 3× looks x86-specific.

One thing that may interest you independently of this PR: with azure-ac present the bench goes bimodal — 6,850,655 min against a 6,978,267 median, same binary, same machine. That ~128k swing is why one commit on this branch passed the gate and the others failed.

I don't have a way to make this cheaper from inside the provider. How would you like me to handle it?

jdx commented Jul 29, 2026

Copy link
Copy Markdown
Owner

I investigated the performance gate in detail. The reported schema increase of 2.96% is inflated by a bimodal benchmark subject, not by the Azure provider implementation.

Cachegrind profiles show that the higher mode is dominated by glibc malloc_consolidate/unlink_chunk and Tokio worker parking/shutdown. With TOKIO_WORKER_THREADS=1, repeated full benchmark runs produced an identical schema count. The remaining Azure schema increase was about 1.06%, comparable to the existing cost of adding one provider variant (~0.95%). Deleting or stubbing the provider body cannot materially improve it because the work is schema construction and serialization.

I opened #670 to stabilize the benchmark: pin Ubuntu 24.04 and Rust 1.91.1, use one Tokio worker, start a compatible new measurement series, and set the gate to 1.25% so intentional provider-schema growth fits without masking larger regressions.

My recommendation is not to require an Azure-specific performance optimization here. Once #670 lands and establishes the new baseline, this PR should rerun/rebase against it. This is independent of Greptile's test_connection() response-validation finding, which remains a correctness issue to address separately.

AI-assisted — Tool: Codex; model: unavailable/unavailable; version: unavailable.

A 200 from a reverse proxy or captive portal was accepted as a healthy
store. Deserialising the list payload is what proves the endpoint is
App Configuration.
@jdx
jdx enabled auto-merge (squash) July 29, 2026 18:02
@jdx
jdx merged commit 34f0ded into jdx:main Jul 29, 2026
16 of 17 checks passed
@jmoreno11
jmoreno11 deleted the feat/azure-app-configuration branch July 29, 2026 18:19
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.

2 participants