feat: Enhance Authority Identity Provider Registry with Bootstrap Capability

- Added support for bootstrap providers in AuthorityIdentityProviderRegistry.
- Introduced a new property for bootstrap providers and updated AggregateCapabilities.
- Updated relevant methods to handle bootstrap capabilities during provider registration.

feat: Introduce Sealed Mode Status in OpenIddict Handlers

- Added SealedModeStatusProperty to AuthorityOpenIddictConstants.
- Enhanced ValidateClientCredentialsHandler, ValidatePasswordGrantHandler, and ValidateRefreshTokenGrantHandler to validate sealed mode evidence.
- Implemented logic to handle airgap seal confirmation requirements.

feat: Update Program Configuration for Sealed Mode

- Registered IAuthoritySealedModeEvidenceValidator in Program.cs.
- Added logging for bootstrap capabilities in identity provider plugins.
- Implemented checks for bootstrap support in API endpoints.

chore: Update Tasks and Documentation

- Marked AUTH-MTLS-11-002 as DONE in TASKS.md.
- Updated documentation to reflect changes in sealed mode and bootstrap capabilities.

fix: Improve CLI Command Handlers Output

- Enhanced output formatting for command responses and prompts in CommandHandlers.cs.

feat: Extend Advisory AI Models

- Added Response property to AdvisoryPipelineOutputModel for better output handling.

fix: Adjust Concelier Web Service Authentication

- Improved JWT token handling in Concelier Web Service to ensure proper token extraction and logging.

test: Enhance Web Service Endpoints Tests

- Added detailed logging for authentication failures in WebServiceEndpointsTests.
- Enabled PII logging for better debugging of authentication issues.

feat: Introduce Air-Gap Configuration Options

- Added AuthorityAirGapOptions and AuthoritySealedModeOptions to StellaOpsAuthorityOptions.
- Implemented validation logic for air-gap configurations to ensure proper setup.
This commit is contained in:
master
2025-11-09 12:18:14 +02:00
parent d71c81e45d
commit ba4c935182
68 changed files with 2142 additions and 291 deletions

View File

@@ -14,6 +14,7 @@ using StellaOps.AdvisoryAI.Prompting;
using StellaOps.AdvisoryAI.Queue;
using StellaOps.AdvisoryAI.Metrics;
using StellaOps.AdvisoryAI.Tools;
using StellaOps.AdvisoryAI.Inference;
using Xunit;
namespace StellaOps.AdvisoryAI.Tests;
@@ -30,7 +31,8 @@ public sealed class AdvisoryPipelineExecutorTests : IDisposable
var guardrail = new StubGuardrailPipeline(blocked: false);
var store = new InMemoryAdvisoryOutputStore();
using var metrics = new AdvisoryPipelineMetrics(_meterFactory);
var executor = new AdvisoryPipelineExecutor(assembler, guardrail, store, metrics, TimeProvider.System);
var inference = new StubInferenceClient();
var executor = new AdvisoryPipelineExecutor(assembler, guardrail, store, metrics, TimeProvider.System, inference);
var message = new AdvisoryTaskQueueMessage(plan.CacheKey, plan.Request);
await executor.ExecuteAsync(plan, message, planFromCache: false, CancellationToken.None);
@@ -43,6 +45,7 @@ public sealed class AdvisoryPipelineExecutorTests : IDisposable
saved.Provenance.InputDigest.Should().Be(plan.CacheKey);
saved.Provenance.OutputHash.Should().NotBeNullOrWhiteSpace();
saved.Prompt.Should().Be("{\"prompt\":\"value\"}");
saved.Response.Should().Be("{\"prompt\":\"value\"}");
saved.Guardrail.Metadata.Should().ContainKey("prompt_length");
}
@@ -54,7 +57,8 @@ public sealed class AdvisoryPipelineExecutorTests : IDisposable
var guardrail = new StubGuardrailPipeline(blocked: true);
var store = new InMemoryAdvisoryOutputStore();
using var metrics = new AdvisoryPipelineMetrics(_meterFactory);
var executor = new AdvisoryPipelineExecutor(assembler, guardrail, store, metrics, TimeProvider.System);
var inference = new StubInferenceClient();
var executor = new AdvisoryPipelineExecutor(assembler, guardrail, store, metrics, TimeProvider.System, inference);
var message = new AdvisoryTaskQueueMessage(plan.CacheKey, plan.Request);
await executor.ExecuteAsync(plan, message, planFromCache: true, CancellationToken.None);
@@ -84,12 +88,12 @@ public sealed class AdvisoryPipelineExecutorTests : IDisposable
listener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
{
doubleMeasurements.Add((instrument.Name, measurement, tags));
doubleMeasurements.Add((instrument.Name, measurement, tags.ToArray()));
});
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
{
longMeasurements.Add((instrument.Name, measurement, tags));
longMeasurements.Add((instrument.Name, measurement, tags.ToArray()));
});
listener.Start();
@@ -99,7 +103,8 @@ public sealed class AdvisoryPipelineExecutorTests : IDisposable
var guardrail = new StubGuardrailPipeline(blocked: true);
var store = new InMemoryAdvisoryOutputStore();
using var metrics = new AdvisoryPipelineMetrics(_meterFactory);
var executor = new AdvisoryPipelineExecutor(assembler, guardrail, store, metrics, TimeProvider.System);
var inference = new StubInferenceClient();
var executor = new AdvisoryPipelineExecutor(assembler, guardrail, store, metrics, TimeProvider.System, inference);
var message = new AdvisoryTaskQueueMessage(plan.CacheKey, plan.Request);
await executor.ExecuteAsync(plan, message, planFromCache: false, CancellationToken.None);
@@ -135,7 +140,7 @@ public sealed class AdvisoryPipelineExecutorTests : IDisposable
listener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
{
doubleMeasurements.Add((instrument.Name, measurement, tags));
doubleMeasurements.Add((instrument.Name, measurement, tags.ToArray()));
});
listener.Start();
@@ -145,7 +150,8 @@ public sealed class AdvisoryPipelineExecutorTests : IDisposable
var guardrail = new StubGuardrailPipeline(blocked: false);
var store = new InMemoryAdvisoryOutputStore();
using var metrics = new AdvisoryPipelineMetrics(_meterFactory);
var executor = new AdvisoryPipelineExecutor(assembler, guardrail, store, metrics, TimeProvider.System);
var inference = new StubInferenceClient();
var executor = new AdvisoryPipelineExecutor(assembler, guardrail, store, metrics, TimeProvider.System, inference);
var message = new AdvisoryTaskQueueMessage(plan.CacheKey, plan.Request);
await executor.ExecuteAsync(plan, message, planFromCache: false, CancellationToken.None);
@@ -289,6 +295,18 @@ public sealed class AdvisoryPipelineExecutorTests : IDisposable
=> Task.FromResult(_result);
}
private sealed class StubInferenceClient : IAdvisoryInferenceClient
{
public AdvisoryInferenceResult Result { get; set; } = AdvisoryInferenceResult.FromLocal("{\"prompt\":\"value\"}");
public Task<AdvisoryInferenceResult> GenerateAsync(
AdvisoryTaskPlan plan,
AdvisoryPrompt prompt,
AdvisoryGuardrailResult guardrailResult,
CancellationToken cancellationToken)
=> Task.FromResult(Result);
}
public void Dispose()
{
_meterFactory.Dispose();

View File

@@ -73,7 +73,7 @@ public sealed class ConcelierAdvisoryDocumentProviderTests
public Task<IReadOnlyList<AdvisoryRawRecord>> FindByAdvisoryKeyAsync(
string tenant,
IReadOnlyCollection<string> searchValues,
string advisoryKey,
IReadOnlyCollection<string> sourceVendors,
CancellationToken cancellationToken)
=> Task.FromResult(_records);

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.AdvisoryAI.Abstractions;
using StellaOps.AdvisoryAI.Caching;
using StellaOps.AdvisoryAI.Context;
using StellaOps.AdvisoryAI.Documents;
@@ -14,6 +15,7 @@ using StellaOps.AdvisoryAI.Guardrails;
using StellaOps.AdvisoryAI.Hosting;
using StellaOps.AdvisoryAI.Outputs;
using StellaOps.AdvisoryAI.Orchestration;
using StellaOps.AdvisoryAI.Prompting;
using StellaOps.AdvisoryAI.Tools;
using Xunit;
@@ -67,11 +69,13 @@ public sealed class FileSystemAdvisoryPersistenceTests : IDisposable
var plan = CreatePlan("cache-abc");
var prompt = "{\"prompt\":\"value\"}";
var guardrail = AdvisoryGuardrailResult.Allowed(prompt);
var response = "response-text";
var output = new AdvisoryPipelineOutput(
plan.CacheKey,
plan.Request.TaskType,
plan.Request.Profile,
prompt,
response,
ImmutableArray.Create(new AdvisoryPromptCitation(1, "doc-1", "chunk-1")),
ImmutableDictionary<string, string>.Empty.Add("advisory_key", plan.Request.AdvisoryKey),
guardrail,
@@ -84,6 +88,7 @@ public sealed class FileSystemAdvisoryPersistenceTests : IDisposable
reloaded.Should().NotBeNull();
reloaded!.Prompt.Should().Be(prompt);
reloaded.Response.Should().Be(response);
reloaded.Metadata.Should().ContainKey("advisory_key").WhoseValue.Should().Be(plan.Request.AdvisoryKey);
}