Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
**/bin
**/obj
.azure
.git
.github
.serena
.claude
tools
24 changes: 22 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.0.x'

Expand All @@ -27,3 +27,23 @@ jobs:
- name: Test
run: dotnet test --no-build --verbosity normal
if: hashFiles('**/*Tests*.csproj') != ''

- name: Build infrastructure template
run: az bicep build --file infra/main.bicep

- name: Validate provisioning hook syntax
shell: pwsh
run: |
$tokens = $null
$errors = $null
[Management.Automation.Language.Parser]::ParseFile(
"$PWD/scripts/complete-container-app-provision.ps1",
[ref]$tokens,
[ref]$errors) | Out-Null
if ($errors.Count -gt 0) {
$errors | ForEach-Object { Write-Error $_ }
exit 1
}

- name: Build container image
run: docker build --file src/DevBrain.Server/Dockerfile .
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ bld/
[Ll]og/
[Ll]ogs/

# Azure Functions
local.settings.json

# Azure Developer CLI
.azure/

Expand Down
60 changes: 47 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,61 @@ All notable changes to DevBrain are tracked in this file. Versions follow [Seman

## [Unreleased]

## [2.0.0] — 2026-09-29

DevBrain 2.0 replaces the Azure Functions host with a standalone ASP.NET Core host on Azure Container Apps. The MCP endpoint moves from `/runtime/webhooks/mcp` on the Function App to `/mcp` on the Container App. Existing documents carry over unchanged, and each client signs in once against the new endpoint. See "Upgrading from 1.x" in the README for the cutover and cleanup steps.

### Added

- Added `DevBrain.Server`, a .NET 10 Minimal API host using `ModelContextProtocol.AspNetCore` and the 2026-07-28 MCP specification revision. Its Streamable HTTP transport is stateless and exposed at `/mcp`.
- Added a shared `DevBrain.Core` library for the Cosmos document services and the OAuth/DCR implementation.
- Added application-owned ASP.NET Core authentication for the complete MCP protocol surface. Unauthenticated MCP requests receive `401` with a `WWW-Authenticate: Bearer resource_metadata="..."` challenge, which removes the Functions host-layer blocker for clients such as VS Code and GitHub Copilot. VS Code / GitHub Copilot now completes OAuth and works end to end.
- Added single-tenant `DevBrain.User` app-role enforcement. Validated Entra role claims are persisted with the local OAuth session and rehydrated into the caller principal.
- Added an anonymous `/healthz` process/readiness endpoint, configurable per-caller rate limiting, an explicit request-body limit, and optional explicit-origin CORS configuration.
- Added Azure Container Apps infrastructure with native HTTPS ingress, a Basic Azure Container Registry, managed identity, Key Vault secret references, Application Insights through Azure Monitor OpenTelemetry, and `0`–`3` replica defaults at `0.5` vCPU / `1 GiB`.
- Added optional secure `ENTRA_CLIENT_SECRET` and `JWT_SIGNING_SECRET` azd values that seed the two Key Vault secrets on first provision, so a new deployment needs no separate Key Vault data-plane step.
- Added an `OAUTH_REDIRECT_URI` provisioning output with the exact `/callback` URL to register on the Entra app.
- Added server integration tests for `/healthz`, protected-resource metadata, the MCP authentication challenge, app-role authorization, and all 12 published tool contracts. The RFC 6749/7591/8414/9728 wire-format guards now run against the server's OAuth response types.

### Removed

- **Removed the Azure Functions host.** Deleted the `DevBrain.Functions` project and its Function App, Flex Consumption plan, managed-identity role assignments, `deploymentpackage` and 1.x `dataprotection-keys` blob containers, the azd `api` service, and the post-provision Functions warm-up wait. `azd provision` is incremental, so existing 1.x environments must delete the old Function App and plan manually (see the README).
- Removed the Functions-only packages, including `Microsoft.ApplicationInsights.WorkerService`, `Microsoft.Azure.Functions.Worker.*`, `Microsoft.Azure.Functions.Worker.Extensions.Mcp`, and `Microsoft.Extensions.Azure`.
- Removed the Functions-vs-server tool contract parity test and the Functions-only `McpToolContextAccessor` tests.
- Removed the one-shot `/ops/touch` maintenance endpoint and its `TouchAllAsync` store operation.
- Removed the legacy function-key/SSE seed script; seed documents through any authenticated MCP client with `UpsertDocument`.

### Fixed

- Hardened OAuth diagnostics against log forging and accidental secret disclosure. Caller-controlled identifiers are now represented by fixed-length SHA-256 fingerprints or bounded structural flags, and Cosmos cleanup warnings no longer include raw OAuth keys or exception messages. Regression tests cover authorization, callback, registration, and token requests containing injected line breaks.
- Hardened the OAuth `refresh_token` grant for MCP clients that retry or restart while their local credential cache is catching up to token rotation. A successful refresh now leaves a short replay marker for the old refresh token, so an immediate retry returns the same replacement refresh token instead of forcing a reconnect. Wrong-client refresh attempts are rejected without consuming the legitimate client's token, and successful refresh/replay calls slide the upstream token vault TTL forward with the local refresh window.
- Restored upstream Entra refresh on the first use of each rotated local refresh token. Tenant ID, object ID, UPN, and `roles` claims are revalidated before the next local access token is accepted; a failed or identity-changing refresh revokes the local upstream session.
- Added the 2026-07-28 authorization hardening used by the DCR flow: `application_type` registration metadata, loopback-only HTTP redirects, RFC 8707 `resource` binding across authorization/token/refresh requests, and the RFC 9207 `iss` authorization-response parameter.
- Added Bicep validation for the required Entra tenant/client parameters so `azd provision` fails fast instead of blanking `OAuth__EntraTenantId` or `OAuth__EntraClientId`.
- Configured the Container App to use its system-assigned identity when pulling private images from ACR, so `azd deploy` no longer fails after a successful remote image build.
- Added a bounded, idempotent post-provision retry for the Container App's Cosmos DB data-role assignment. This handles the short Entra replication window that can otherwise reject a newly created managed identity during its first deployment.
- Preserved azd's last deployed Container App image during later infrastructure-only provisions instead of resetting a healthy revision to the public first-provision placeholder.

### Changed
- Added reason-specific server-side diagnostics for OAuth refresh failures. `TokenHandler/refresh` now logs a stable rejection reason (`missing`, `expired`, `replay_window_expired`, `wrong_client`, `upstream_missing_or_expired`, etc.) plus short SHA-256 refresh-token fingerprints so stale per-session client credential generations can be correlated without logging token material.
- Updated the compatibility notes for the modern unified ChatGPT/Codex Windows app, which is currently working well with DevBrain OAuth but remains under monitoring rather than being marked fully resolved.
- Added optional OAuth token-window settings for deployments that need a different refresh cadence or replay tolerance: `OAUTH_ACCESS_TOKEN_LIFETIME_MINUTES` and `OAUTH_REFRESH_REPLAY_LIFETIME_MINUTES` flow through Bicep to `OAuth__AccessTokenLifetimeMinutes` and `OAuth__RefreshReplayLifetimeMinutes`. Values must be whole minutes from 1 through 1,440. If left unset, DevBrain uses its built-in defaults: 10 minutes for access tokens and 5 minutes for refresh replay markers.
- Refreshed the deployed runtime dependency stack to current compatible NuGet releases, including `Microsoft.Azure.Functions.Worker` 2.52.0, `Microsoft.Azure.Functions.Worker.Extensions.Mcp` 1.6.0, `Microsoft.ApplicationInsights.WorkerService` 2.23.0, `Microsoft.Azure.Cosmos` 3.60.0, `Microsoft.Extensions.Azure` 1.14.0, IdentityModel 8.18.0, `Microsoft.AspNetCore.DataProtection` 10.0.8, and `System.Security.Cryptography.Xml` 10.0.10. Removed the unused direct `ModelContextProtocol` package reference.
- Kept Application Insights on the direct Azure Functions isolated worker integration path (`AddApplicationInsightsTelemetryWorkerService` + `ConfigureFunctionsApplicationInsights`) instead of moving to the newer OpenTelemetry telemetry wiring.
- Refreshed test tooling to `Microsoft.NET.Test.Sdk` 18.6.0, `xunit.runner.visualstudio` 3.1.5, and `Microsoft.Extensions.TimeProvider.Testing` 10.6.0.
- Synced the release notes with merged Dependabot PR #19, which already moved `Microsoft.AspNetCore.DataProtection` and `System.Security.Cryptography.Xml` to 10.0.7.
- Patched the remaining Azure Data Protection helper packages to `Azure.Extensions.AspNetCore.DataProtection.Blobs` 1.5.2 and `Azure.Extensions.AspNetCore.DataProtection.Keys` 1.6.2, then replaced the stale 10.0.6 workaround comment in the project file.

- The release version now lives only in `Directory.Build.props`. The MCP `serverInfo` version and startup log read it from the assembly instead of a hard-coded string.
- Kept the `dataprotection-keys-v2` key ring container and the `v2:` OAuth-state key prefix names, so existing deployments keep their live sessions across the Functions retirement.
- Rewrote the README deploy guide as a step-by-step new-tenant runbook: Entra app registration (portal and CLI) including the `DevBrain.User` app role, user assignment, provision, redirect URI, deploy, verify, first-run seeding, upgrading from 1.x, and operating notes (secret rotation, cold starts, cost, purge-protected Key Vault on teardown). Local development now documents every required setting as .NET user secrets.
- Added reason-specific server-side diagnostics for OAuth refresh failures. The refresh handler logs a stable rejection reason (`missing`, `expired`, `replay_window_expired`, `wrong_client`, `upstream_missing_or_expired`, etc.) plus short SHA-256 refresh-token fingerprints, so stale per-session client credential generations can be correlated without logging token material.
- Added optional OAuth token-window settings for deployments that need a different refresh cadence or replay tolerance. `OAUTH_ACCESS_TOKEN_LIFETIME_MINUTES` and `OAUTH_REFRESH_REPLAY_LIFETIME_MINUTES` flow through Bicep to `OAuth__AccessTokenLifetimeMinutes` and `OAuth__RefreshReplayLifetimeMinutes`. Values must be whole minutes from 1 through 1,440. If they're unset, DevBrain uses its built-in defaults: 10 minutes for access tokens and 5 minutes for refresh replay markers.
- Updated the compatibility notes for the modern unified ChatGPT/Codex Windows app, which works well with DevBrain OAuth but remains under monitoring.
- Refreshed the runtime dependency stack to current releases: `ModelContextProtocol.AspNetCore` 2.2.0, `Microsoft.Azure.Cosmos` 3.63.1, IdentityModel 8.23.0, `Microsoft.AspNetCore.DataProtection` 10.0.12, `System.Security.Cryptography.Xml` 10.0.12, `Azure.Extensions.AspNetCore.DataProtection.Blobs` 1.5.4, and `Azure.Extensions.AspNetCore.DataProtection.Keys` 1.6.4.
- Migrated both test projects from the deprecated `xunit` 2.9.3 to `xunit.v3` 4.0.1 on Microsoft.Testing.Platform, opted in via a new root `global.json`. `Microsoft.NET.Test.Sdk` and `xunit.runner.visualstudio` are no longer referenced. Renamed `DevBrain.Functions.Tests` to `DevBrain.Core.Tests`.
- Bumped CI to `actions/checkout@v7` and `actions/setup-dotnet@v6` (Node 24 runtime).
- Added `.serena/` to `.gitignore` so local Serena workspace metadata stays out of the public repository.

### Validation
- `dotnet list devbrain.slnx package --vulnerable --include-transitive` reports no vulnerable packages.
- `dotnet list devbrain.slnx package --outdated --highest-patch` reports no patch-level updates for direct package references.
- `dotnet list devbrain.slnx package --outdated --include-transitive` was checked; direct package references are current except the intentional `Microsoft.ApplicationInsights.WorkerService` 2.x hold for the existing Functions Application Insights integration path, with upstream-owned transitive package updates still reported.
- `dotnet list devbrain.slnx package --deprecated` reports no deprecated packages in `DevBrain.Functions`; the remaining deprecation is the test-only `xunit` 2.9.3 package, which requires a separate xUnit v3 migration.
- `dotnet test devbrain.slnx` passes with 148 tests.

- `dotnet test --solution devbrain.slnx` passes 157 tests across `DevBrain.Core.Tests` and `DevBrain.Server.Tests`.
- `dotnet list devbrain.slnx package --outdated` reports every direct reference current.
- `dotnet list devbrain.slnx package --vulnerable --include-transitive` and `--deprecated --include-transitive` report no vulnerable or deprecated packages in any project.
- `az bicep build --file infra/main.bicep` succeeds, the post-provision hook parses cleanly, and the container image builds from `src/DevBrain.Server/Dockerfile`.
- The Container Apps host is deployed and serving the production environment. The unified ChatGPT/Codex Windows app completed OAuth with `DevBrain.User`, discovered all 12 tools, and read existing shared Cosmos documents. VS Code / GitHub Copilot completes OAuth and works end to end.

## [1.9.0] — 2026-04-15

Expand Down
26 changes: 12 additions & 14 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ Thanks for your interest in contributing to DevBrain!

1. **Prerequisites**
- [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0)
- [Azure Functions Core Tools v4](https://learn.microsoft.com/azure/azure-functions/functions-run-local)
- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) (logged in via `az login`)
- A Cosmos DB account (or the [Cosmos DB Emulator](https://learn.microsoft.com/azure/cosmos-db/local-emulator))
- A dev DevBrain environment to point at (Cosmos DB, storage, and Key Vault). The server authenticates to them with `DefaultAzureCredential`, so the key-based Cosmos DB Emulator isn't supported.

2. **Clone and build**
```bash
Expand All @@ -18,31 +17,30 @@ Thanks for your interest in contributing to DevBrain!
```

3. **Configure local settings**
```bash
cp src/DevBrain.Functions/local.settings.json.example src/DevBrain.Functions/local.settings.json
```
Edit `local.settings.json` with your Cosmos DB account endpoint.

Configure the required settings with .NET user secrets or environment variables. The server fails fast when any required value is missing. [Local Development](README.md#local-development) in the README lists every setting and the roles your `az login` identity needs.

4. **Run locally**
```bash
cd src/DevBrain.Functions
func start
dotnet run --project src/DevBrain.Server
```

The MCP endpoint is `/mcp` and the anonymous health endpoint is `/healthz`.

## Pull Request Process

1. Fork the repository and create a feature branch from `main`.
2. Make your changes. Keep commits focused and atomic.
3. Ensure `dotnet build` completes with no warnings (warnings are treated as errors).
3. Ensure `dotnet build` completes with no warnings (warnings are treated as errors) and `dotnet test --solution devbrain.slnx` passes. Tests run on xUnit v3 through Microsoft.Testing.Platform, which `global.json` opts into.
4. For dependency changes, check the whole solution from the repository root before opening the PR:
```bash
dotnet list devbrain.slnx package --vulnerable --include-transitive
dotnet list devbrain.slnx package --outdated --highest-patch
dotnet list devbrain.slnx package --outdated --include-transitive
dotnet list devbrain.slnx package --deprecated
dotnet list devbrain.slnx package --outdated
dotnet list devbrain.slnx package --deprecated --include-transitive
```
5. Open a pull request against `main` with a clear description of the change.
6. A maintainer will review and merge once CI passes.
5. For a release, bump `<Version>` in `Directory.Build.props`. It's the single source for assembly versions and the MCP `serverInfo` version. Then add the matching `CHANGELOG.md` entry.
6. Open a pull request against `main` with a clear description of the change.
7. A maintainer will review and merge once CI passes.

## Code Style

Expand Down
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<PropertyGroup>
<Version>1.0.0</Version>
<!-- Single source of truth for the DevBrain release version (assemblies and MCP serverInfo). -->
<Version>2.0.0</Version>
<Authors>Ignite Solutions Group</Authors>
<Company>Ignite Solutions Group</Company>
<Product>DevBrain</Product>
Expand Down
Loading
Loading