From 9075bad2d901448a3b67c1f7c30cb3c802fc99d2 Mon Sep 17 00:00:00 2001 From: master <> Date: Mon, 17 Nov 2025 21:21:56 +0200 Subject: [PATCH] Add unit tests and implementations for MongoDB index models and OpenAPI metadata - Implemented `MongoIndexModelTests` to verify index models for various stores. - Created `OpenApiMetadataFactory` with methods to generate OpenAPI metadata. - Added tests for `OpenApiMetadataFactory` to ensure expected defaults and URL overrides. - Introduced `ObserverSurfaceSecrets` and `WebhookSurfaceSecrets` for managing secrets. - Developed `RuntimeSurfaceFsClient` and `WebhookSurfaceFsClient` for manifest retrieval. - Added dependency injection tests for `SurfaceEnvironmentRegistration` in both Observer and Webhook contexts. - Implemented tests for secret resolution in `ObserverSurfaceSecretsTests` and `WebhookSurfaceSecretsTests`. - Created `EnsureLinkNotMergeCollectionsMigrationTests` to validate MongoDB migration logic. - Added project files for MongoDB tests and NuGet package mirroring. --- docs/api/notify-openapi.yaml | 501 + docs/api/notify-pack-approvals.yaml | 122 + docs/api/notify-sdk-examples.md | 137 + ...PRINT_0110_0001_0001_ingestion_evidence.md | 91 + .../SPRINT_0111_0001_0001_advisoryai.md | 64 + .../SPRINT_0112_0001_0001_concelier_i.md | 4 +- .../SPRINT_0119_0001_0001_excititor_i.md | 14 +- .../SPRINT_0119_0001_0002_excititor_ii.md | 71 + .../SPRINT_0119_0001_0003_excititor_iii.md | 60 + .../SPRINT_0119_0001_0004_excititor_iv.md | 60 + .../SPRINT_0119_0001_0005_excititor_v.md | 60 + .../SPRINT_0119_0001_0006_excititor_vi.md | 59 + .../SPRINT_0120_0000_0001_policy_reasoning.md | 6 +- ...RINT_0138_0000_0001_scanner_ruby_parity.md | 64 + ..._0144_0001_0001_zastava_runtime_signals.md | 60 + docs/implplan/SPRINT_131_scanner_surface.md | 5 +- docs/implplan/SPRINT_140_runtime_signals.md | 27 +- docs/implplan/blocked-all.md | 151 + docs/implplan/tasks-all.md | 20 +- docs/modules/cli/guides/cli-reference.md | 24 + .../concelier/link-not-merge-schema.md | 125 + .../excititor/operations/evidence-api.md | 66 + .../scanner/operations/entrytrace-cadence.md | 40 + docs/samples/lnm/linkset-ghsa.json | 20 + docs/samples/lnm/observation-ghsa.json | 24 + ...t-expiry-warning.slack.en-us.template.json | 16 + ...-api-deprecation.email.en-us.template.json | 16 + ...-api-deprecation.slack.en-us.template.json | 16 + .../telemetry/dashboards/ledger/alerts.yml | 39 + .../ledger/ledger-observability.json | 91 + .../20251106-task-runner-baseline.mongosh | 125 + .../coverage.cobertura.xml | 71696 +++++++++++++++ .../coverage.cobertura.xml | 71698 ++++++++++++++++ out/tools/pack.binlog | Bin 0 -> 453067 bytes .../provenance/build-statement-sample.json | 24 + src/Concelier/AGENTS.md | 50 + .../Linksets/AdvisoryLinkset.cs | 50 + .../AdvisoryLinksetBackfillService.cs | 82 + .../Linksets/AdvisoryLinksetCursor.cs | 5 + .../Linksets/AdvisoryLinksetNormalization.cs | 78 + .../Linksets/AdvisoryLinksetQueryOptions.cs | 10 + .../Linksets/AdvisoryLinksetQueryService.cs | 111 + .../IAdvisoryLinksetBackfillService.cs | 9 + .../Linksets/IAdvisoryLinksetSink.cs | 9 + .../Linksets/IAdvisoryLinksetStore.cs | 20 + ...tionPipelineServiceCollectionExtensions.cs | 45 + .../Observations/IAdvisoryObservationSink.cs | 10 + .../Raw/AdvisoryRawService.cs | 69 +- .../Linksets/AdvisoryLinksetDocument.cs | 87 + .../Linksets/AdvisoryLinksetSink.cs | 22 + .../Linksets/AdvisoryLinksetStore.cs | 170 + .../EnsureLinkNotMergeCollectionsMigration.cs | 242 + .../Observations/AdvisoryObservationSink.cs | 22 + .../ServiceCollectionExtensions.cs | 7 + .../AdvisoryLinksetQueryServiceTests.cs | 94 + .../WebServiceEndpointsTests.cs | 144 + .../Contracts/VexEvidenceChunkContracts.cs | 44 + .../StellaOps.Excititor.WebService/Program.cs | 69 +- .../Services/VexEvidenceChunkService.cs | 130 + .../Properties/AssemblyInfo.cs | 3 + .../Observations/TimelineEvent.cs | 76 + .../VexAttestationPayload.cs | 99 + .../IVexAttestationLinkStore.cs | 12 + .../MongoVexAttestationLinkStore.cs | 43 + .../VexAttestationLinkRecord.cs | 63 + .../VexMongoMappingRegistry.cs | 5 +- .../Observations/TimelineEventTests.cs | 41 + .../VexAttestationPayloadTests.cs | 15 + .../VexAttestationLinkEndpointTests.cs | 86 + .../VexEvidenceChunkServiceTests.cs | 117 + .../VexEvidenceChunksEndpointTests.cs | 128 + .../Compatibility/IsExternalInit.cs | 7 + .../fixtures/sample-small.ndjson | 2 + .../tools/LedgerReplayHarness/.placeholder | 0 .../LedgerReplayHarness.csproj | 15 + .../tools/LedgerReplayHarness/Program.cs | 502 + .../scripts/compute_hashes.py | 43 + .../HarnessRunnerTests.cs | 37 + .../LedgerMetricsTests.cs | 223 + .../LedgerReplayHarness/HarnessRunner.cs | 148 + .../tools/LedgerReplayHarness/HarnessStats.cs | 26 + .../LedgerReplayHarness/ILedgerClient.cs | 8 + .../InMemoryLedgerClient.cs | 15 + .../LedgerReplayHarness.csproj | 14 + .../LedgerReplayHarness/MerkleCalculator.cs | 41 + .../tools/LedgerReplayHarness/Program.cs | 22 + .../LedgerReplayHarness/TaskThrottler.cs | 36 + .../AttestationTemplateCoverageTests.cs | 77 + .../DeprecationTemplateTests.cs | 66 + .../OpenApiEndpointTests.cs | 87 + .../Support/InMemoryAuditRepository.cs | 30 + .../Support/InMemoryPackApprovalRepository.cs | 18 + .../Contracts/PackApprovalRequest.cs | 45 + .../StellaOps.Notifier.WebService/Program.cs | 165 +- .../Setup/OpenApiDocumentCache.cs | 28 + .../Setup/WebServiceAssemblyMarker.cs | 6 + .../openapi/notify-openapi.yaml | 501 + src/Notifier/StellaOps.Notifier/TASKS.md | 15 + .../docs/NOTIFY-OAS-61-ETAG.md | 15 + .../Documents/PackApprovalDocument.cs | 49 + .../EnsurePackApprovalsCollectionMigration.cs | 34 + .../EnsurePackApprovalsIndexesMigration.cs | 41 + .../INotifyPackApprovalRepository.cs | 8 + .../NotifyPackApprovalRepository.cs | 29 + .../Directory.Build.props | 5 + .../Program.cs | 60 + ...ellaOps.Provenance.Attestation.Tool.csproj | 14 + .../tmpfile.txt | 1 + .../BuildModels.cs | 113 + .../StellaOps.Provenance.Attestation/Hex.cs | 20 + .../PromotionAttestation.cs | 21 + .../Signers.cs | 107 + .../StellaOps.Provenance.Attestation.csproj | 9 + .../Verification.cs | 35 + .../CanonicalJsonTests.cs | 27 + .../HexTests.cs | 21 + .../MerkleTreeTests.cs | 38 + .../PromotionAttestationBuilderTests.cs | 26 + .../SignerTests.cs | 47 + ...llaOps.Provenance.Attestation.Tests.csproj | 14 + .../VerificationTests.cs | 42 + .../Internal/DotNetEntrypointResolver.cs | 215 + .../Core/AnalysisSnapshot.cs | 7 + .../LanguageComponentEvidenceExtensions.cs | 15 + .../Core/LanguageEntrypointRecord.cs | 96 + .../StellaOps.Scanner.EntryTrace/TASKS.md | 5 + .../DotNet/DotNetEntrypointResolverTests.cs | 51 + .../StellaOps.Scheduler.Worker/TASKS.md | 9 + ...PolicyRunDispatchBackgroundServiceTests.cs | 130 + .../MongoIndexModelTests.cs | 62 + .../OpenApiMetadataFactoryTests.cs | 27 + .../OpenApiMetadataFactory.cs | 38 + .../Secrets/ObserverSurfaceSecrets.cs | 58 + .../Surface/RuntimeSurfaceFsClient.cs | 32 + .../Secrets/WebhookSurfaceSecrets.cs | 43 + .../Surface/WebhookSurfaceFsClient.cs | 65 + .../SurfaceEnvironmentRegistrationTests.cs | 84 + .../Secrets/ObserverSurfaceSecretsTests.cs | 85 + .../Surface/RuntimeSurfaceFsClientTests.cs | 58 + .../SurfaceEnvironmentRegistrationTests.cs | 83 + .../SurfaceSecretsRegistrationTests.cs | 64 + .../Surface/WebhookSurfaceFsClientTests.cs | 57 + ...reLinkNotMergeCollectionsMigrationTests.cs | 70 + ...laOps.Concelier.Storage.Mongo.Tests.csproj | 21 + tools/nuget-prime/mirror-packages.txt | 31 + tools/nuget-prime/nuget-prime.csproj | 43 + 146 files changed, 152183 insertions(+), 82 deletions(-) create mode 100644 docs/api/notify-openapi.yaml create mode 100644 docs/api/notify-pack-approvals.yaml create mode 100644 docs/api/notify-sdk-examples.md create mode 100644 docs/implplan/SPRINT_0110_0001_0001_ingestion_evidence.md create mode 100644 docs/implplan/SPRINT_0111_0001_0001_advisoryai.md create mode 100644 docs/implplan/SPRINT_0119_0001_0002_excititor_ii.md create mode 100644 docs/implplan/SPRINT_0119_0001_0003_excititor_iii.md create mode 100644 docs/implplan/SPRINT_0119_0001_0004_excititor_iv.md create mode 100644 docs/implplan/SPRINT_0119_0001_0005_excititor_v.md create mode 100644 docs/implplan/SPRINT_0119_0001_0006_excititor_vi.md create mode 100644 docs/implplan/SPRINT_0138_0000_0001_scanner_ruby_parity.md create mode 100644 docs/implplan/SPRINT_0144_0001_0001_zastava_runtime_signals.md create mode 100644 docs/implplan/blocked-all.md create mode 100644 docs/modules/concelier/link-not-merge-schema.md create mode 100644 docs/modules/excititor/operations/evidence-api.md create mode 100644 docs/modules/scanner/operations/entrytrace-cadence.md create mode 100644 docs/samples/lnm/linkset-ghsa.json create mode 100644 docs/samples/lnm/observation-ghsa.json create mode 100644 offline/notifier/templates/attestation/tmpl-attest-expiry-warning.slack.en-us.template.json create mode 100644 offline/notifier/templates/deprecation/tmpl-api-deprecation.email.en-us.template.json create mode 100644 offline/notifier/templates/deprecation/tmpl-api-deprecation.slack.en-us.template.json create mode 100644 offline/telemetry/dashboards/ledger/alerts.yml create mode 100644 offline/telemetry/dashboards/ledger/ledger-observability.json create mode 100644 ops/mongo/taskrunner/20251106-task-runner-baseline.mongosh create mode 100644 out/coverage/ledger/4d714ddd-216e-4643-ba81-2b8a4ffda218/coverage.cobertura.xml create mode 100644 out/coverage/ledger/e4bdc625-4088-44fb-ad98-bb084fa8e84b/coverage.cobertura.xml create mode 100644 out/tools/pack.binlog create mode 100644 samples/provenance/build-statement-sample.json create mode 100644 src/Concelier/AGENTS.md create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinkset.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetBackfillService.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetCursor.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetNormalization.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetQueryOptions.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetQueryService.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetBackfillService.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetSink.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetStore.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/ObservationPipelineServiceCollectionExtensions.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Core/Observations/IAdvisoryObservationSink.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetDocument.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetSink.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetStore.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Migrations/EnsureLinkNotMergeCollectionsMigration.cs create mode 100644 src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Observations/AdvisoryObservationSink.cs create mode 100644 src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/Linksets/AdvisoryLinksetQueryServiceTests.cs create mode 100644 src/Excititor/StellaOps.Excititor.WebService/Contracts/VexEvidenceChunkContracts.cs create mode 100644 src/Excititor/StellaOps.Excititor.WebService/Services/VexEvidenceChunkService.cs create mode 100644 src/Excititor/__Libraries/StellaOps.Excititor.Attestation/Properties/AssemblyInfo.cs create mode 100644 src/Excititor/__Libraries/StellaOps.Excititor.Core/Observations/TimelineEvent.cs create mode 100644 src/Excititor/__Libraries/StellaOps.Excititor.Core/VexAttestationPayload.cs create mode 100644 src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/IVexAttestationLinkStore.cs create mode 100644 src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/MongoVexAttestationLinkStore.cs create mode 100644 src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/VexAttestationLinkRecord.cs create mode 100644 src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/Observations/TimelineEventTests.cs create mode 100644 src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/VexAttestationPayloadTests.cs create mode 100644 src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexAttestationLinkEndpointTests.cs create mode 100644 src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexEvidenceChunkServiceTests.cs create mode 100644 src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexEvidenceChunksEndpointTests.cs create mode 100644 src/Findings/StellaOps.Findings.Ledger/Infrastructure/Compatibility/IsExternalInit.cs create mode 100644 src/Findings/StellaOps.Findings.Ledger/fixtures/sample-small.ndjson create mode 100644 src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/.placeholder create mode 100644 src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/LedgerReplayHarness.csproj create mode 100644 src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/Program.cs create mode 100644 src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/scripts/compute_hashes.py create mode 100644 src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/HarnessRunnerTests.cs create mode 100644 src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/LedgerMetricsTests.cs create mode 100644 src/Findings/tools/LedgerReplayHarness/HarnessRunner.cs create mode 100644 src/Findings/tools/LedgerReplayHarness/HarnessStats.cs create mode 100644 src/Findings/tools/LedgerReplayHarness/ILedgerClient.cs create mode 100644 src/Findings/tools/LedgerReplayHarness/InMemoryLedgerClient.cs create mode 100644 src/Findings/tools/LedgerReplayHarness/LedgerReplayHarness.csproj create mode 100644 src/Findings/tools/LedgerReplayHarness/MerkleCalculator.cs create mode 100644 src/Findings/tools/LedgerReplayHarness/Program.cs create mode 100644 src/Findings/tools/LedgerReplayHarness/TaskThrottler.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/AttestationTemplateCoverageTests.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/DeprecationTemplateTests.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/OpenApiEndpointTests.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/Support/InMemoryAuditRepository.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/Support/InMemoryPackApprovalRepository.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Contracts/PackApprovalRequest.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Setup/OpenApiDocumentCache.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Setup/WebServiceAssemblyMarker.cs create mode 100644 src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/openapi/notify-openapi.yaml create mode 100644 src/Notifier/StellaOps.Notifier/TASKS.md create mode 100644 src/Notifier/StellaOps.Notifier/docs/NOTIFY-OAS-61-ETAG.md create mode 100644 src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Documents/PackApprovalDocument.cs create mode 100644 src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Migrations/EnsurePackApprovalsCollectionMigration.cs create mode 100644 src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Migrations/EnsurePackApprovalsIndexesMigration.cs create mode 100644 src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Repositories/INotifyPackApprovalRepository.cs create mode 100644 src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Repositories/NotifyPackApprovalRepository.cs create mode 100644 src/Provenance/StellaOps.Provenance.Attestation.Tool/Directory.Build.props create mode 100644 src/Provenance/StellaOps.Provenance.Attestation.Tool/Program.cs create mode 100644 src/Provenance/StellaOps.Provenance.Attestation.Tool/StellaOps.Provenance.Attestation.Tool.csproj create mode 100644 src/Provenance/StellaOps.Provenance.Attestation.Tool/tmpfile.txt create mode 100644 src/Provenance/StellaOps.Provenance.Attestation/BuildModels.cs create mode 100644 src/Provenance/StellaOps.Provenance.Attestation/Hex.cs create mode 100644 src/Provenance/StellaOps.Provenance.Attestation/PromotionAttestation.cs create mode 100644 src/Provenance/StellaOps.Provenance.Attestation/Signers.cs create mode 100644 src/Provenance/StellaOps.Provenance.Attestation/StellaOps.Provenance.Attestation.csproj create mode 100644 src/Provenance/StellaOps.Provenance.Attestation/Verification.cs create mode 100644 src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/CanonicalJsonTests.cs create mode 100644 src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/HexTests.cs create mode 100644 src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/MerkleTreeTests.cs create mode 100644 src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/PromotionAttestationBuilderTests.cs create mode 100644 src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/SignerTests.cs create mode 100644 src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/StellaOps.Provenance.Attestation.Tests.csproj create mode 100644 src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/VerificationTests.cs create mode 100644 src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/Internal/DotNetEntrypointResolver.cs create mode 100644 src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/AnalysisSnapshot.cs create mode 100644 src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/LanguageComponentEvidenceExtensions.cs create mode 100644 src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/LanguageEntrypointRecord.cs create mode 100644 src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace/TASKS.md create mode 100644 src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Tests/DotNet/DotNetEntrypointResolverTests.cs create mode 100644 src/Scheduler/__Libraries/StellaOps.Scheduler.Worker/TASKS.md create mode 100644 src/Scheduler/__Tests/StellaOps.Scheduler.Worker.Tests/PolicyRunDispatchBackgroundServiceTests.cs create mode 100644 src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/MongoIndexModelTests.cs create mode 100644 src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/OpenApiMetadataFactoryTests.cs create mode 100644 src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.WebService/OpenApiMetadataFactory.cs create mode 100644 src/Zastava/StellaOps.Zastava.Observer/Secrets/ObserverSurfaceSecrets.cs create mode 100644 src/Zastava/StellaOps.Zastava.Observer/Surface/RuntimeSurfaceFsClient.cs create mode 100644 src/Zastava/StellaOps.Zastava.Webhook/Secrets/WebhookSurfaceSecrets.cs create mode 100644 src/Zastava/StellaOps.Zastava.Webhook/Surface/WebhookSurfaceFsClient.cs create mode 100644 src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/DependencyInjection/SurfaceEnvironmentRegistrationTests.cs create mode 100644 src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/Secrets/ObserverSurfaceSecretsTests.cs create mode 100644 src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/Surface/RuntimeSurfaceFsClientTests.cs create mode 100644 src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/DependencyInjection/SurfaceEnvironmentRegistrationTests.cs create mode 100644 src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/DependencyInjection/SurfaceSecretsRegistrationTests.cs create mode 100644 src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/Surface/WebhookSurfaceFsClientTests.cs create mode 100644 tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests/EnsureLinkNotMergeCollectionsMigrationTests.cs create mode 100644 tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests/StellaOps.Concelier.Storage.Mongo.Tests.csproj create mode 100644 tools/nuget-prime/mirror-packages.txt create mode 100644 tools/nuget-prime/nuget-prime.csproj diff --git a/docs/api/notify-openapi.yaml b/docs/api/notify-openapi.yaml new file mode 100644 index 000000000..f4f3d2f3b --- /dev/null +++ b/docs/api/notify-openapi.yaml @@ -0,0 +1,501 @@ +# OpenAPI 3.1 specification for StellaOps Notifier WebService (draft) +openapi: 3.1.0 +info: + title: StellaOps Notifier API + version: 0.6.0-draft + description: | + Contract for Notifications Studio (Notifier) covering rules, templates, incidents, + and quiet hours. Uses the platform error envelope and tenant header `X-StellaOps-Tenant`. +servers: + - url: https://api.stellaops.example.com + description: Production + - url: https://api.dev.stellaops.example.com + description: Development +security: + - oauth2: [notify.viewer] + - oauth2: [notify.operator] + - oauth2: [notify.admin] +paths: + /api/v1/notify/rules: + get: + summary: List notification rules + tags: [Rules] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/PageToken' + responses: + '200': + description: Paginated rule list + content: + application/json: + schema: + type: object + properties: + items: + type: array + items: { $ref: '#/components/schemas/NotifyRule' } + nextPageToken: + type: string + examples: + default: + value: + items: + - ruleId: rule-critical + tenantId: tenant-dev + name: Critical scanner verdicts + enabled: true + match: + eventKinds: [scanner.report.ready] + minSeverity: critical + actions: + - actionId: act-slack-critical + channel: chn-slack-soc + template: tmpl-critical + digest: instant + nextPageToken: null + default: + $ref: '#/components/responses/Error' + post: + summary: Create a notification rule + tags: [Rules] + parameters: + - $ref: '#/components/parameters/Tenant' + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyRule' } + examples: + create-rule: + value: + ruleId: rule-attest-fail + tenantId: tenant-dev + name: Attestation failures → SOC + enabled: true + match: + eventKinds: [attestor.verification.failed] + actions: + - actionId: act-soc + channel: chn-webhook-soc + template: tmpl-attest-verify-fail + responses: + '201': + description: Rule created + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyRule' } + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/rules/{ruleId}: + get: + summary: Fetch a rule + tags: [Rules] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/RuleId' + responses: + '200': + description: Rule + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyRule' } + default: + $ref: '#/components/responses/Error' + patch: + summary: Update a rule (partial) + tags: [Rules] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/RuleId' + requestBody: + required: true + content: + application/json: + schema: + type: object + description: JSON Merge Patch + responses: + '200': + description: Updated rule + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyRule' } + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/templates: + get: + summary: List templates + tags: [Templates] + parameters: + - $ref: '#/components/parameters/Tenant' + - name: key + in: query + description: Filter by template key + schema: { type: string } + responses: + '200': + description: Templates + content: + application/json: + schema: + type: array + items: { $ref: '#/components/schemas/NotifyTemplate' } + default: + $ref: '#/components/responses/Error' + post: + summary: Create a template + tags: [Templates] + parameters: + - $ref: '#/components/parameters/Tenant' + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyTemplate' } + responses: + '201': + description: Template created + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyTemplate' } + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/templates/{templateId}: + get: + summary: Fetch a template + tags: [Templates] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/TemplateId' + responses: + '200': + description: Template + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyTemplate' } + default: + $ref: '#/components/responses/Error' + patch: + summary: Update a template (partial) + tags: [Templates] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/TemplateId' + requestBody: + required: true + content: + application/json: + schema: + type: object + description: JSON Merge Patch + responses: + '200': + description: Updated template + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyTemplate' } + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/incidents: + get: + summary: List incidents (paged) + tags: [Incidents] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/PageToken' + responses: + '200': + description: Incident page + content: + application/json: + schema: + type: object + properties: + items: + type: array + items: { $ref: '#/components/schemas/Incident' } + nextPageToken: { type: string } + default: + $ref: '#/components/responses/Error' + post: + summary: Raise an incident (ops/toggle/override) + tags: [Incidents] + parameters: + - $ref: '#/components/parameters/Tenant' + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/Incident' } + examples: + start-incident: + value: + incidentId: inc-telemetry-outage + kind: outage + severity: major + startedAt: 2025-11-17T04:02:00Z + shortDescription: "Telemetry pipeline degraded; burn-rate breach" + metadata: + source: slo-evaluator + responses: + '202': + description: Incident accepted + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/incidents/{incidentId}/ack: + post: + summary: Acknowledge an incident notification + tags: [Incidents] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/IncidentId' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + ackToken: + type: string + description: DSSE-signed acknowledgement token + responses: + '204': + description: Acknowledged + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/quiet-hours: + get: + summary: Get quiet-hours schedule + tags: [QuietHours] + parameters: + - $ref: '#/components/parameters/Tenant' + responses: + '200': + description: Quiet hours schedule + content: + application/json: + schema: { $ref: '#/components/schemas/QuietHours' } + examples: + current: + value: + quietHoursId: qh-default + windows: + - timezone: UTC + days: [Mon, Tue, Wed, Thu, Fri] + start: "22:00" + end: "06:00" + exemptions: + - eventKinds: [attestor.verification.failed] + reason: "Always alert for attestation failures" + default: + $ref: '#/components/responses/Error' + post: + summary: Set quiet-hours schedule + tags: [QuietHours] + parameters: + - $ref: '#/components/parameters/Tenant' + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/QuietHours' } + responses: + '200': + description: Updated quiet hours + content: + application/json: + schema: { $ref: '#/components/schemas/QuietHours' } + default: + $ref: '#/components/responses/Error' + +components: + securitySchemes: + oauth2: + type: oauth2 + flows: + clientCredentials: + tokenUrl: https://auth.stellaops.example.com/oauth/token + scopes: + notify.viewer: Read-only Notifier access + notify.operator: Manage rules/templates/incidents within tenant + notify.admin: Tenant-scoped administration + parameters: + Tenant: + name: X-StellaOps-Tenant + in: header + required: true + description: Tenant slug + schema: { type: string } + PageSize: + name: pageSize + in: query + schema: { type: integer, minimum: 1, maximum: 200, default: 50 } + PageToken: + name: pageToken + in: query + schema: { type: string } + RuleId: + name: ruleId + in: path + required: true + schema: { type: string } + TemplateId: + name: templateId + in: path + required: true + schema: { type: string } + IncidentId: + name: incidentId + in: path + required: true + schema: { type: string } + + responses: + Error: + description: Standard error envelope + content: + application/json: + schema: { $ref: '#/components/schemas/ErrorEnvelope' } + examples: + validation: + value: + error: + code: validation_failed + message: "quietHours.windows[0].start must be HH:mm" + traceId: "f62f3c2b9c8e4c53" + + schemas: + ErrorEnvelope: + type: object + required: [error] + properties: + error: + type: object + required: [code, message, traceId] + properties: + code: { type: string } + message: { type: string } + traceId: { type: string } + + NotifyRule: + type: object + required: [ruleId, tenantId, name, match, actions] + properties: + ruleId: { type: string } + tenantId: { type: string } + name: { type: string } + description: { type: string } + enabled: { type: boolean, default: true } + match: { $ref: '#/components/schemas/RuleMatch' } + actions: + type: array + items: { $ref: '#/components/schemas/RuleAction' } + labels: + type: object + additionalProperties: { type: string } + metadata: + type: object + additionalProperties: { type: string } + + RuleMatch: + type: object + properties: + eventKinds: + type: array + items: { type: string } + minSeverity: { type: string, enum: [info, low, medium, high, critical] } + verdicts: + type: array + items: { type: string } + labels: + type: array + items: { type: string } + kevOnly: { type: boolean } + + RuleAction: + type: object + required: [actionId, channel] + properties: + actionId: { type: string } + channel: { type: string } + template: { type: string } + digest: { type: string, description: "Digest window key e.g. instant|5m|15m|1h|1d" } + throttle: { type: string, description: "ISO-8601 duration, e.g. PT5M" } + locale: { type: string } + enabled: { type: boolean, default: true } + metadata: + type: object + additionalProperties: { type: string } + + NotifyTemplate: + type: object + required: [templateId, tenantId, key, channelType, locale, body, renderMode, format] + properties: + templateId: { type: string } + tenantId: { type: string } + key: { type: string } + channelType: { type: string, enum: [slack, teams, email, webhook, custom] } + locale: { type: string, description: "BCP-47, lower-case" } + renderMode: { type: string, enum: [Markdown, Html, AdaptiveCard, PlainText, Json] } + format: { type: string, enum: [slack, teams, email, webhook, json] } + description: { type: string } + body: { type: string } + metadata: + type: object + additionalProperties: { type: string } + + Incident: + type: object + required: [incidentId, kind, severity, startedAt] + properties: + incidentId: { type: string } + kind: { type: string, description: "outage|degradation|security|ops-drill" } + severity: { type: string, enum: [minor, major, critical] } + startedAt: { type: string, format: date-time } + endedAt: { type: string, format: date-time } + shortDescription: { type: string } + description: { type: string } + metadata: + type: object + additionalProperties: { type: string } + + QuietHours: + type: object + required: [quietHoursId, windows] + properties: + quietHoursId: { type: string } + windows: + type: array + items: { $ref: '#/components/schemas/QuietHoursWindow' } + exemptions: + type: array + description: Event kinds that bypass quiet hours + items: + type: object + properties: + eventKinds: + type: array + items: { type: string } + reason: { type: string } + + QuietHoursWindow: + type: object + required: [timezone, days, start, end] + properties: + timezone: { type: string, description: "IANA TZ, e.g., UTC" } + days: + type: array + items: + type: string + enum: [Mon, Tue, Wed, Thu, Fri, Sat, Sun] + start: { type: string, description: "HH:mm" } + end: { type: string, description: "HH:mm" } diff --git a/docs/api/notify-pack-approvals.yaml b/docs/api/notify-pack-approvals.yaml new file mode 100644 index 000000000..640a611b2 --- /dev/null +++ b/docs/api/notify-pack-approvals.yaml @@ -0,0 +1,122 @@ +openapi: 3.1.0 +info: + title: Notifier Pack Approvals Ingestion (fragment) + version: 0.1.0-draft + description: > + Contract for ingesting pack approval/policy decisions emitted by Task Runner and Policy Engine. + Served under Notifier WebService. +paths: + /api/v1/notify/pack-approvals: + post: + summary: Ingest pack approval decision + operationId: ingestPackApproval + tags: [PackApprovals] + security: + - oauth2: [notify.operator] + - hmac: [] + parameters: + - name: X-StellaOps-Tenant + in: header + required: true + schema: { type: string } + - name: Idempotency-Key + in: header + required: true + description: Stable UUID to dedupe retries. + schema: { type: string, format: uuid } + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/PackApprovalEvent' } + examples: + approval-granted: + value: + eventId: "20e4e5fe-3d4a-4f57-9f9b-b1a1c1111111" + issuedAt: "2025-11-17T16:00:00Z" + kind: "pack.approval.granted" + packId: "offline-kit-2025-11" + policy: + id: "policy-123" + version: "v5" + decision: "approved" + actor: "task-runner" + resumeToken: "rt-abc123" + summary: "All required attestations verified." + labels: + environment: "prod" + approver: "ops" + responses: + '202': + description: Accepted; durable write queued for processing. + headers: + X-Resume-After: + description: Resume token echo or replacement + schema: { type: string } + default: + $ref: '#/components/responses/Error' + +components: + securitySchemes: + oauth2: + type: oauth2 + flows: + clientCredentials: + tokenUrl: https://auth.stellaops.example.com/oauth/token + scopes: + notify.operator: Ingest approval events + hmac: + type: http + scheme: bearer + description: Pre-shared HMAC token (air-gap friendly) referenced by secretRef. + + schemas: + PackApprovalEvent: + type: object + required: + - eventId + - issuedAt + - kind + - packId + - decision + - actor + properties: + eventId: { type: string, format: uuid } + issuedAt: { type: string, format: date-time } + kind: + type: string + enum: [pack.approval.granted, pack.approval.denied, pack.policy.override] + packId: { type: string } + policy: + type: object + properties: + id: { type: string } + version: { type: string } + decision: + type: string + enum: [approved, denied, overridden] + actor: { type: string } + resumeToken: + type: string + description: Opaque token for at-least-once resume. + summary: { type: string } + labels: + type: object + additionalProperties: { type: string } + + responses: + Error: + description: Error envelope + content: + application/json: + schema: + type: object + required: [error] + properties: + error: + type: object + required: [code, message, traceId] + properties: + code: { type: string } + message: { type: string } + traceId: { type: string } diff --git a/docs/api/notify-sdk-examples.md b/docs/api/notify-sdk-examples.md new file mode 100644 index 000000000..564260d0c --- /dev/null +++ b/docs/api/notify-sdk-examples.md @@ -0,0 +1,137 @@ +# Notifier SDK Usage Examples (rules, incidents, quiet hours) + +> Work of this type must also be applied everywhere it should be applied. Keep examples air-gap friendly and deterministic. + +## Prerequisites +- Token with scopes: `notify.viewer` for reads, `notify.operator` for writes (tenant-scoped). +- Tenant header: `X-StellaOps-Tenant: `. +- Base URL: `https://api.stellaops.example.com`. +- OpenAPI document: `/.well-known/openapi` (served by Notifier). + +## Rules CRUD +### cURL +```bash +# Create rule +curl -X POST "$BASE/api/v1/notify/rules" \ + -H "Authorization: Bearer $TOKEN" \ + -H "X-StellaOps-Tenant: acme-prod" \ + -H "Content-Type: application/json" \ + -d '{ + "ruleId": "rule-attest-fail", + "tenantId": "acme-prod", + "name": "Attestation failures to SOC", + "match": { "eventKinds": ["attestor.verification.failed"] }, + "actions": [{ + "actionId": "act-soc", + "channel": "chn-soc-webhook", + "template": "tmpl-attest-verify-fail", + "digest": "instant" + }] + }' + +# List rules (paginated) +curl -H "Authorization: Bearer $TOKEN" \ + -H "X-StellaOps-Tenant: acme-prod" \ + "$BASE/api/v1/notify/rules?pageSize=50" +``` + +### TypeScript (OpenAPI-generated client) +```ts +import { RulesApi, Configuration } from "./generated/notify-client"; + +const api = new RulesApi(new Configuration({ + basePath: process.env.BASE, + accessToken: process.env.TOKEN +})); + +await api.createRule({ + xStellaOpsTenant: "acme-prod", + notifyRule: { + ruleId: "rule-attest-fail", + tenantId: "acme-prod", + name: "Attestation failures to SOC", + match: { eventKinds: ["attestor.verification.failed"] }, + actions: [{ + actionId: "act-soc", + channel: "chn-soc-webhook", + template: "tmpl-attest-verify-fail", + digest: "instant" + }] + } +}); +``` + +### Python (OpenAPI-generated client) +```python +from notify_client import RulesApi, Configuration, ApiClient + +config = Configuration(host=BASE, access_token=TOKEN) +with ApiClient(config) as client: + api = RulesApi(client) + api.create_rule( + x_stella_ops_tenant="acme-prod", + notify_rule={ + "ruleId": "rule-attest-fail", + "tenantId": "acme-prod", + "name": "Attestation failures to SOC", + "match": {"eventKinds": ["attestor.verification.failed"]}, + "actions": [{ + "actionId": "act-soc", + "channel": "chn-soc-webhook", + "template": "tmpl-attest-verify-fail", + "digest": "instant" + }] + } + ) +``` + +## Incident acknowledge +### cURL +```bash +curl -X POST "$BASE/api/v1/notify/incidents/inc-telemetry/ack" \ + -H "Authorization: Bearer $TOKEN" \ + -H "X-StellaOps-Tenant: acme-prod" \ + -H "Content-Type: application/json" \ + -d '{"ackToken":""}' \ + -i +``` + +### TypeScript +```ts +import { IncidentsApi } from "./generated/notify-client"; +await new IncidentsApi(config).ackIncident({ + incidentId: "inc-telemetry", + xStellaOpsTenant: "acme-prod", + inlineObject: { ackToken: process.env.ACK_TOKEN } +}); +``` + +## Quiet hours +### cURL +```bash +curl -X POST "$BASE/api/v1/notify/quiet-hours" \ + -H "Authorization: Bearer $TOKEN" \ + -H "X-StellaOps-Tenant: acme-prod" \ + -H "Content-Type: application/json" \ + -d '{ + "quietHoursId": "qh-default", + "windows": [{ + "timezone": "UTC", + "days": ["Mon","Tue","Wed","Thu","Fri"], + "start": "22:00", + "end": "06:00" + }], + "exemptions": [{ + "eventKinds": ["attestor.verification.failed"], + "reason": "Always alert on attestation failures" + }] + }' +``` + +## Smoke-test recipe (SDK CI) +- Generate client from `/.well-known/openapi` (ts/python/go) with deterministic options. +- Run: + 1) create rule → list rules → delete rule. + 2) set quiet hours → get quiet hours. + 3) ack incident with dummy token (expect 2xx or validation error envelope). +- Assert deterministic headers: `X-OpenAPI-Scope=notify`, `ETag` stable for identical spec bytes. diff --git a/docs/implplan/SPRINT_0110_0001_0001_ingestion_evidence.md b/docs/implplan/SPRINT_0110_0001_0001_ingestion_evidence.md new file mode 100644 index 000000000..0f3a0d3c1 --- /dev/null +++ b/docs/implplan/SPRINT_0110_0001_0001_ingestion_evidence.md @@ -0,0 +1,91 @@ +# Sprint 0110-0001-0001 · Ingestion & Evidence (Phase 110) + +## Topic & Scope +- Finalise Advisory AI guardrail evidence (docs, SBOM feeds, policy knobs) without blocking customer rollout. +- Land Concelier structured caching + telemetry so Link-Not-Merge schemas feed consoles, air-gap bundles, and attestations. +- Prepare Excititor chunk API/telemetry/attestation contracts for deterministic VEX evidence delivery. +- Staff and kick off Mirror assembler (DSSE/TUF metadata, OCI/time anchors, CLI/Export Center automation). +- Working directories: `src/AdvisoryAI`, `src/Concelier`, `src/Excititor`, `ops/devops` (Mirror assembler). + +## Dependencies & Concurrency +- Upstream: Sprint 0100.A (Attestor) must stay green; Link-Not-Merge schema set (`CONCELIER-LNM-21-*`, `CARTO-GRAPH-21-002`) gates Concelier/Excititor work. Advisory AI docs depend on SBOM/CLI/Policy/DevOps artefacts (`SBOM-AIAI-31-001`, `CLI-VULN-29-001`, `CLI-VEX-30-001`, `POLICY-ENGINE-31-001`, `DEVOPS-AIAI-31-001`). +- Parallelism: Sprints in the 0110 decade must remain independent; avoid new intra-decade dependencies. +- Evidence Locker contract and Mirror staffing decisions gate attestation work and Mirror tracks respectively. + +## Documentation Prerequisites +- docs/modules/advisory-ai/architecture.md +- docs/modules/concelier/architecture.md +- docs/modules/excititor/architecture.md +- docs/modules/export-center/architecture.md +- docs/modules/airgap/architecture.md (timeline + bundle requirements) + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | DOCS-AIAI-31-004 | DOING | CONSOLE-VULN-29-001; CONSOLE-VEX-30-001; SBOM-AIAI-31-001/003 | Docs Guild · Console Guild | Guardrail console doc; screenshots + SBOM evidence pending. | +| 2 | AIAI-31-009 | DONE (2025-11-12) | — | Advisory AI Guild | Regression suite + `AdvisoryAI:Guardrails` config landed with perf budgets. | +| 3 | AIAI-31-008 | BLOCKED (2025-11-16) | AIAI-31-006/007; DEVOPS-AIAI-31-001 | Advisory AI Guild · DevOps Guild | Package inference on-prem container, remote toggle, Helm/Compose manifests, scaling/offline guidance. | +| 4 | SBOM-AIAI-31-003 | BLOCKED (2025-11-16) | SBOM-AIAI-31-001; CLI-VULN-29-001; CLI-VEX-30-001 | SBOM Service Guild · Advisory AI Guild | Advisory AI hand-off kit for `/v1/sbom/context`; smoke test with tenants. | +| 5 | DOCS-AIAI-31-005/006/008/009 | BLOCKED | CLI-VULN-29-001; CLI-VEX-30-001; POLICY-ENGINE-31-001; DEVOPS-AIAI-31-001 | Docs Guild | CLI/policy/ops docs paused pending upstream artefacts. | +| 6 | CONCELIER-AIAI-31-002 | DOING | CONCELIER-GRAPH-21-001/002; CARTO-GRAPH-21-002 (Link-Not-Merge) | Concelier Core · WebService Guilds | LNM schema drafted (`docs/modules/concelier/link-not-merge-schema.md`) + sample payloads; wiring can proceed while review runs. | +| 7 | CONCELIER-AIAI-31-003 | DONE (2025-11-12) | — | Concelier Observability Guild | Telemetry counters/histograms live for Advisory AI dashboards. | +| 8 | CONCELIER-AIRGAP-56-001..58-001 | BLOCKED | Link-Not-Merge schema; Evidence Locker contract | Concelier Core · AirGap Guilds | Mirror/offline provenance chain. | +| 9 | CONCELIER-CONSOLE-23-001..003 | BLOCKED | Link-Not-Merge schema | Concelier Console Guild | Console advisory aggregation/search helpers. | +| 10 | CONCELIER-ATTEST-73-001/002 | BLOCKED | CONCELIER-AIAI-31-002; Evidence Locker contract | Concelier Core · Evidence Locker Guild | Attestation inputs + transparency metadata. | +| 11 | FEEDCONN-ICSCISA-02-012 / KISA-02-008 | BLOCKED | Feed owner remediation plan | Concelier Feed Owners | Overdue provenance refreshes. | +| 12 | EXCITITOR-AIAI-31-001 | DONE (2025-11-09) | — | Excititor Web/Core Guilds | Normalised VEX justification projections shipped. | +| 13 | EXCITITOR-AIAI-31-002 | BLOCKED | Link-Not-Merge schema; Evidence Locker contract | Excititor Web/Core Guilds | Chunk API for Advisory AI feeds. | +| 14 | EXCITITOR-AIAI-31-003 | BLOCKED | EXCITITOR-AIAI-31-002 | Excititor Observability Guild | Telemetry gated on chunk API. | +| 15 | EXCITITOR-AIAI-31-004 | BLOCKED | EXCITITOR-AIAI-31-002 | Docs Guild · Excititor Guild | Chunk API docs. | +| 16 | EXCITITOR-ATTEST-01-003 / 73-001 / 73-002 | BLOCKED | EXCITITOR-AIAI-31-002; Evidence Locker contract | Excititor Guild · Evidence Locker Guild | Attestation scope + payloads. | +| 17 | EXCITITOR-AIRGAP-56/57/58 · CONN-TRUST-01-001 | BLOCKED | Link-Not-Merge schema; attestation plan | Excititor Guild · AirGap Guilds | Air-gap ingest + connector trust tasks. | +| 18 | MIRROR-CRT-56-001 | BLOCKED | Staffing decision overdue | Mirror Creator Guild | Kickoff slipped past 2025-11-15. | +| 19 | MIRROR-CRT-56-002 | BLOCKED | MIRROR-CRT-56-001; PROV-OBS-53-001 | Mirror Creator · Security Guilds | Needs assembler owner first. | +| 20 | MIRROR-CRT-57-001/002 | BLOCKED | MIRROR-CRT-56-001; AIRGAP-TIME-57-001 | Mirror Creator Guild · AirGap Time Guild | Waiting on staffing. | +| 21 | MIRROR-CRT-58-001/002 | BLOCKED | MIRROR-CRT-56-001; EXPORT-OBS-54-001; CLI-AIRGAP-56-001 | Mirror Creator · CLI · Exporter Guilds | Requires assembler staffing + upstream contracts. | +| 22 | EXPORT-OBS-51-001 / 54-001 · AIRGAP-TIME-57-001 · CLI-AIRGAP-56-001 · PROV-OBS-53-001 | BLOCKED | MIRROR-CRT-56-001 ownership | Exporter Guild · AirGap Time · CLI Guild | Blocked until assembler staffed. | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-09 | Captured initial wave scope, interlocks, risks for SBOM/CLI/Policy/DevOps artefacts, Link-Not-Merge schemas, Excititor justification backlog, Mirror commitments. | Sprint 110 leads | +| 2025-11-13 | Refreshed tracker ahead of 14–15 Nov checkpoints; outstanding asks: SBOM/CLI/Policy/DevOps ETAs, Link-Not-Merge approval, Mirror staffing. | Sprint 110 leads | +| 2025-11-16 | Updated task board: marked Advisory AI packaging, Concelier air-gap/console/attestation tracks, Excititor chunk/attestation/air-gap tracks, and all Mirror tracks as BLOCKED pending schema approvals, Evidence Locker contract, Mirror staffing decisions. | Implementer | +| 2025-11-16 | Drafted LNM schema + samples (`docs/modules/concelier/link-not-merge-schema.md`, `docs/samples/lnm/*`); moved CONCELIER-AIAI-31-002 to DOING pending review; added migration + tests to Mongo storage. | Implementer | +| 2025-11-17 | Wired LNM ingestion writes: observations+linksets persisted via Mongo sinks, WebService DI updated, build green. Next: expose read APIs and backfill. | Implementer | +| 2025-11-17 | Added cursor-paged `/linksets` API with normalized purls/versions; implemented linkset lookup/paging + unit test coverage. | Implementer | +| 2025-11-17 | Persisted normalized linksets (purls/versions) in ingestion/backfill; added /linksets integration tests for normalized fields and cursor paging. Full solution test run aborted mid-build; rerun targeted Concelier WebService tests. | Implementer | +| 2025-11-17 | Targeted `/linksets` WebService tests invoked; `dotnet test` fails early with MSBuild switch `--no-restore,workdir:` injected by toolchain, so tests remain pending until runner is fixed. | Implementer | +| 2025-11-16 | Normalised sprint file to standard template and renamed from `SPRINT_110_ingestion_evidence.md` to `SPRINT_0110_0001_0001_ingestion_evidence.md`; no semantic changes. | Planning | + +## Decisions & Risks +### Decisions in flight +| Decision | Blocking work | Accountable owner(s) | Due date | +| --- | --- | --- | --- | +| Confirm SBOM/CLI/Policy/DevOps delivery dates | DOCS-AIAI backlog, SBOM-AIAI-31-003, AIAI-31-008 | SBOM Service · CLI · Policy · DevOps guild leads | 2025-11-14 | +| Approve Link-Not-Merge schema (`CONCELIER-GRAPH-21-001/002`, `CARTO-GRAPH-21-002`) | CONCELIER-AIAI-31-002; EXCITITOR-AIAI-31-002/003/004; air-gap + attestation tasks | Concelier Core · Cartographer Guild · SBOM Service Guild | 2025-11-14 | +| Review & ratify drafted LNM schema doc (`docs/modules/concelier/link-not-merge-schema.md`) | CONCELIER-AIAI-31-002 | Concelier Core · Architecture Guild | 2025-11-17 | +| Assign MIRROR-CRT-56-001 owner | Entire Mirror wave + Export Center + AirGap Time automation | Mirror Creator Guild · Exporter Guild · AirGap Time Guild | 2025-11-15 | +| Evidence Locker attestation scope sign-off | EXCITITOR-ATTEST-01-003/73-001/73-002; CONCELIER-ATTEST-73-001/002 | Evidence Locker Guild · Excititor Guild · Concelier Guild | 2025-11-15 | +| Approve DOCS-AIAI-31-004 screenshot plan | Publication of console guardrail doc | Docs Guild · Console Guild | 2025-11-15 | + +### Risk outlook (2025-11-13) +| Risk | Impact | Mitigation / owner | +| --- | --- | --- | +| SBOM/CLI/Policy/DevOps artefacts slip past 2025-11-14 | Advisory AI docs + SBOM feeds stay blocked, delaying rollout & dependent sprints. | Lock ETAs during 14 Nov interlock; escalate to Advisory AI leadership if commitments slip. | +| Link-Not-Merge schema approval delayed | Concelier/Excititor APIs, console overlays, air-gap bundles remain gated. | Close 14 Nov review with migration notes; unblock tasks immediately after approval. | +| Excititor attestation backlog stalls | VEX evidence + air-gap parity cannot progress; Mirror support drifts. | Use 15 Nov sequencing session to lock order and reserve engineering capacity. | +| MIRROR-CRT-56-001 remains unstaffed | DSSE/TUF, OCI/time-anchor, CLI, Export Center automation cannot start (Sprint 0125 slips). | Assign owner at kickoff; reallocate Export/AirGap engineers if needed. | +| Connector refreshes (ICSCISA/KISA) remain overdue | Advisory AI may serve stale advisories; telemetry accuracy suffers. | Feed owners to publish remediation plan + interim mitigations by 2025-11-15 stand-up. | +| Concelier WebService tests blocked by injected MSBuild switch `workdir:` | Cannot validate new `/linksets` integration; release confidence reduced. | Fix runner/tooling or execute tests in environment that does not append `workdir:` to MSBuild args. | + +## Next Checkpoints +| Date (UTC) | Session | Goal | Impacted wave(s) | Prep owner(s) | +| --- | --- | --- | --- | --- | +| 2025-11-14 | Advisory AI customer surfaces follow-up | Capture SBOM/CLI/Policy/DevOps ETAs to restart DOCS/SBOM work. | 110.A | Advisory AI · SBOM · CLI · Policy · DevOps guild leads | +| 2025-11-14 | Link-Not-Merge schema review | Approve schema payloads + migration notes. | 110.B · 110.C | Concelier Core · Cartographer Guild · SBOM Service Guild | +| 2025-11-15 | Excititor attestation sequencing | Lock Evidence Locker contract + backlog order. | 110.C | Excititor Web/Core · Evidence Locker Guild | +| 2025-11-15 | Mirror evidence kickoff | Assign MIRROR-CRT-56-001 owner; confirm staffing; outline DSSE/TUF + OCI milestones. | 110.D | Mirror Creator · Exporter · AirGap Time · Security guilds | + +## Appendix +- Detailed coordination artefacts, contingency playbook, and historical notes live at `docs/implplan/archived/SPRINT_110_ingestion_evidence_2025-11-13.md`. diff --git a/docs/implplan/SPRINT_0111_0001_0001_advisoryai.md b/docs/implplan/SPRINT_0111_0001_0001_advisoryai.md new file mode 100644 index 000000000..d1ce322e7 --- /dev/null +++ b/docs/implplan/SPRINT_0111_0001_0001_advisoryai.md @@ -0,0 +1,64 @@ +# Sprint 0111-0001-0001 · Advisory AI — Ingestion & Evidence (Phase 110.A) + +## Topic & Scope +- Advance Advisory AI docs, packaging, and SBOM hand-off while keeping upstream console/CLI/policy dependencies explicit. +- Maintain Link-Not-Merge alignment for advisory evidence feeding Advisory AI surfaces. +- Working directory: `src/AdvisoryAI` and `docs` (Advisory AI docs). + +## Dependencies & Concurrency +- Depends on Sprint 0100.A (Attestor) remaining green. +- Console/CLI/SBOM/DevOps artefacts: `CONSOLE-VULN-29-001`, `CONSOLE-VEX-30-001`, `EXCITITOR-CONSOLE-23-001`, `SBOM-AIAI-31-001`, `CLI-VULN-29-001`, `CLI-VEX-30-001`, `DEVOPS-AIAI-31-001`. +- Link-Not-Merge schema (`CONCELIER-LNM-21-*`) provides canonical advisory evidence; keep sequencing with Concelier sprints. + +## Documentation Prerequisites +- docs/README.md; docs/07_HIGH_LEVEL_ARCHITECTURE.md +- docs/modules/platform/architecture-overview.md +- docs/modules/advisory-ai/architecture.md + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | DOCS-AIAI-31-006 | DONE (2025-11-13) | — | Docs Guild · Policy Guild (`docs`) | `docs/policy/assistant-parameters.md` documents inference modes, guardrail phrases, budgets, cache/queue knobs (POLICY-ENGINE-31-001 inputs via `AdvisoryAiServiceOptions`). | +| 2 | DOCS-AIAI-31-008 | BLOCKED (2025-11-03) | SBOM-AIAI-31-001 | Docs Guild · SBOM Service Guild (`docs`) | Publish `/docs/sbom/remediation-heuristics.md` (feasibility scoring, blast radius). | +| 3 | DOCS-AIAI-31-009 | BLOCKED (2025-11-03) | DEVOPS-AIAI-31-001 | Docs Guild · DevOps Guild (`docs`) | Create `/docs/runbooks/assistant-ops.md` for warmup, cache priming, outages, scaling. | +| 4 | SBOM-AIAI-31-003 | BLOCKED (2025-11-16) | SBOM-AIAI-31-001 | SBOM Service Guild · Advisory AI Guild (`src/SbomService/StellaOps.SbomService`) | Publish Advisory AI hand-off kit for `/v1/sbom/context`, provide base URL/API key + tenant header contract, run smoke test. | +| 5 | AIAI-31-008 | BLOCKED (2025-11-16) | AIAI-31-006/007; DEVOPS-AIAI-31-001 | Advisory AI Guild · DevOps Guild (`src/AdvisoryAI/StellaOps.AdvisoryAI`) | Package inference on-prem container, remote toggle, Helm/Compose manifests, scaling/offline guidance. | +| 6 | AIAI-31-009 | DONE (2025-11-12) | — | Advisory AI Guild · QA Guild (`src/AdvisoryAI/StellaOps.AdvisoryAI`) | Develop unit/golden/property/perf tests, injection harness, regression suite; determinism with seeded caches. | +| 7 | DOCS-AIAI-31-004 | BLOCKED (2025-11-16) | CONSOLE-VULN-29-001; CONSOLE-VEX-30-001; EXCITITOR-CONSOLE-23-001 | Docs Guild · Console Guild (`docs`) | `/docs/advisory-ai/console.md` screenshots, a11y, copy-as-ticket instructions. | +| 8 | DOCS-AIAI-31-005 | BLOCKED (2025-11-03) | CLI-VULN-29-001; CLI-VEX-30-001; AIAI-31-004C | Docs Guild · CLI Guild (`docs`) | Publish `/docs/advisory-ai/cli.md` covering commands, exit codes, scripting patterns. | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-02 | Structured + vector retrievers landed; deterministic CSAF/OSV/Markdown chunkers with hash embeddings and tests. | Advisory AI Guild | +| 2025-11-03 | DOCS-AIAI-31-001/002/003 published; DOCS-AIAI-31-004 marked BLOCKED (console widgets pending); DOCS-AIAI-31-005/008/009 blocked; SBOM models finalized; WebService/Worker scaffolds created. | Docs Guild | +| 2025-11-04 | AIAI-31-002/003 completed; WebService/Worker queue wiring emits metrics; SBOM address flows via `SbomContextClientOptions.BaseAddress`; orchestrator cache keys expanded. | Advisory AI Guild | +| 2025-11-07 | DOCS-AIAI-31-004 draft committed with workflow outline; screenshots pending widget delivery. | Docs Guild | +| 2025-11-08 | Console endpoints staffed; guardrail/inference sections documented; screenshot placeholders remain. | Docs Guild | +| 2025-11-09 | Guardrail pipeline enforcement tests landed. | Advisory AI Guild | +| 2025-11-12 | AIAI-31-009 test suite completed. | Advisory AI Guild | +| 2025-11-13 | DOCS-AIAI-31-006 published (`assistant-parameters.md`). | Docs Guild | +| 2025-11-16 | SBOM-AIAI-31-003 and AIAI-31-008 marked BLOCKED pending SBOM-AIAI-31-001 and DEVOPS-AIAI-31-001 respectively; DOCS-AIAI-31-004 remains BLOCKED pending Console/Excititor feeds. | Planner | +| 2025-11-16 | Normalised sprint file to standard template and renamed from `SPRINT_111_advisoryai.md` to `SPRINT_0111_0001_0001_advisoryai.md`; no semantic changes. | Planning | + +## Decisions & Risks +- Console dependencies (CONSOLE-VULN-29-001, CONSOLE-VEX-30-001, EXCITITOR-CONSOLE-23-001) control closure of DOCS-AIAI-31-004; consider temporary mock screenshots if dates slip. +- SBOM-AIAI-31-001 is gate for SBOM hand-off kit and remediation heuristics doc. +- CLI backlog (CLI-VULN-29-001 / CLI-VEX-30-001) blocks CLI doc; request interim outputs if priorities shift. +- DevOps runbook (DEVOPS-AIAI-31-001) needed before packaging (AIAI-31-008) proceeds. + +## Next Checkpoints +- 2025-11-14: Console owners to confirm widget readiness for DOCS-AIAI-31-004. +- 2025-11-14: SBOM-AIAI-31-001 projection kit ETA to unlock SBOM-AIAI-31-003/DOCS-AIAI-31-008. +- 2025-11-15: CLI owners to share `stella advise` verb outline/beta timeline. +- 2025-11-15: DevOps to share draft for DEVOPS-AIAI-31-001 to unblock AIAI-31-008/DOCS-AIAI-31-009. + +## Blockers & Dependencies (detailed) +| Blocked item | Dependency | Owner(s) | Notes | +| --- | --- | --- | --- | +| DOCS-AIAI-31-004 (`/docs/advisory-ai/console.md`) | CONSOLE-VULN-29-001; CONSOLE-VEX-30-001; EXCITITOR-CONSOLE-23-001 | Docs Guild · Console Guild | Screenshots + a11y copy pending widgets/feeds. | +| DOCS-AIAI-31-005 (`/docs/advisory-ai/cli.md`) | CLI-VULN-29-001; CLI-VEX-30-001; AIAI-31-004C | Docs Guild · CLI Guild | CLI verbs/outputs unavailable; doc paused. | +| DOCS-AIAI-31-008 (`/docs/sbom/remediation-heuristics.md`) | SBOM-AIAI-31-001 | Docs Guild · SBOM Service Guild | Needs heuristics kit + contract. | +| DOCS-AIAI-31-009 (`/docs/runbooks/assistant-ops.md`) | DEVOPS-AIAI-31-001 | Docs Guild · DevOps Guild | Runbook steps pending. | +| SBOM-AIAI-31-003 (`/v1/sbom/context` hand-off kit) | SBOM-AIAI-31-001 | SBOM Service Guild · Advisory AI Guild | Requires projection + smoke plan. | +| AIAI-31-008 (on-prem/remote inference packaging) | AIAI-31-006..007; DEVOPS-AIAI-31-001 | Advisory AI Guild · DevOps Guild | Packaging waits for guardrail knob doc (done) + DevOps runbook draft. | diff --git a/docs/implplan/SPRINT_0112_0001_0001_concelier_i.md b/docs/implplan/SPRINT_0112_0001_0001_concelier_i.md index 56eb0216a..82e1394bb 100644 --- a/docs/implplan/SPRINT_0112_0001_0001_concelier_i.md +++ b/docs/implplan/SPRINT_0112_0001_0001_concelier_i.md @@ -54,6 +54,9 @@ | 2025-11-12 | CONCELIER-AIAI-31-003 shipped OTEL counters for Advisory AI chunk traffic (cache hit ratios + guardrail blocks per tenant). | Concelier WebService Guild | | 2025-11-13 | Rebaseline: locked structured field scope to canonical model + provenance anchors aligned to competitor schemas. | Planning | | 2025-11-16 | Normalised sprint file to standard template and renamed from `SPRINT_112_concelier_i.md` to `SPRINT_0112_0001_0001_concelier_i.md`; no semantic changes. | Planning | +| 2025-11-17 | Created Concelier module charter at `src/Concelier/AGENTS.md`; unblocked Workstreams B–E and reset tasks to TODO. | Concelier Implementer | +| 2025-11-17 | Added authority/tenant enforcement smoke tests for ingest + observations; CONCELIER-CORE-AOC-19-013 blocked by storage DI ambiguity (`IAdvisoryLinksetStore`). | Concelier Implementer | +| 2025-11-17 | Retried build after renaming Mongo linkset store and redoing DI; ambiguity persists (`IAdvisoryLinksetStore`), WebService tests still not runnable. | Concelier Implementer | ## Decisions & Risks - Link-Not-Merge schema slip past 2025-11-14 would stall Workstreams A and D; fallback adapter prep required. @@ -75,4 +78,3 @@ | MIRROR-CRT-56-001 staffing | Workstream B (AIRGAP-56/57/58) | Mirror Creator Guild · Exporter Guild · AirGap Time Guild | Owner not assigned (per Sprint 110); kickoff on 2025-11-15 must resolve. | | Evidence Locker attestation contract | Workstream C (ATTEST-73) | Evidence Locker Guild · Concelier Core | Needs alignment with Excititor attestation plan on 2025-11-15. | | Authority scope smoke coverage (`CONCELIER-CORE-AOC-19-013`) | Workstream E | Concelier Core · Authority Guild | Waiting on structured endpoint readiness + AUTH-SIG-26-001 validation. | - diff --git a/docs/implplan/SPRINT_0119_0001_0001_excititor_i.md b/docs/implplan/SPRINT_0119_0001_0001_excititor_i.md index d88b138db..374f5de30 100644 --- a/docs/implplan/SPRINT_0119_0001_0001_excititor_i.md +++ b/docs/implplan/SPRINT_0119_0001_0001_excititor_i.md @@ -23,15 +23,15 @@ | # | Task ID | Status | Key dependency / next step | Owners | Task Definition | | --- | --- | --- | --- | --- | --- | | 1 | EXCITITOR-AIAI-31-001 | DONE (2025-11-12) | Available to Advisory AI; monitor usage. | Excititor WebService Guild | Expose normalized VEX justifications, scope trees, and anchors via `VexObservation` projections so Advisory AI can cite raw evidence without consensus logic. | -| 2 | EXCITITOR-AIAI-31-002 | TODO | Start `/vex/evidence/chunks`; reuse 31-001 outputs. | Excititor WebService Guild | Stream raw statements + signature metadata with tenant/policy filters for RAG clients; aggregation-only, reference observation/linkset IDs. | -| 3 | EXCITITOR-AIAI-31-003 | DOING (in review 2025-11-13) | Await Ops span sink; finalize metrics wiring. | Excititor WebService Guild · Observability Guild | Instrument evidence APIs with request counters, chunk histograms, signature-failure + AOC guard-violation meters. | -| 4 | EXCITITOR-AIAI-31-004 | TODO | Finalize OpenAPI/SDK/docs once 31-002/003 stabilize. | Excititor WebService Guild · Docs Guild | Codify Advisory-AI evidence contract, determinism guarantees, and mapping of observation IDs to storage. | +| 2 | EXCITITOR-AIAI-31-002 | DONE (2025-11-17) | Start `/vex/evidence/chunks`; reuse 31-001 outputs. | Excititor WebService Guild | Stream raw statements + signature metadata with tenant/policy filters for RAG clients; aggregation-only, reference observation/linkset IDs. | +| 3 | EXCITITOR-AIAI-31-003 | BLOCKED (2025-11-17) | Await Ops span sink; finalize metrics wiring. | Excititor WebService Guild · Observability Guild | Instrument evidence APIs with request counters, chunk histograms, signature-failure + AOC guard-violation meters. | +| 4 | EXCITITOR-AIAI-31-004 | BLOCKED (2025-11-17) | Waiting for 31-003 telemetry sink to stabilize before finalizing docs/SDK. | Excititor WebService Guild · Docs Guild | Codify Advisory-AI evidence contract, determinism guarantees, and mapping of observation IDs to storage. | | 5 | EXCITITOR-AIRGAP-56-001 | TODO | Waiting on Export Center mirror bundle schema (Sprint 162). | Excititor Core Guild | Mirror-first ingestion that preserves upstream digests, bundle IDs, and provenance for offline parity. | | 6 | EXCITITOR-AIRGAP-57-001 | TODO | Blocked on 56-001; define sealed-mode errors. | Excititor Core Guild · AirGap Policy Guild | Enforce sealed-mode policies, remediation errors, and staleness annotations surfaced to Advisory AI. | | 7 | EXCITITOR-AIRGAP-58-001 | TODO | Depends on 57-001 and EvidenceLocker portable format (160/161). | Excititor Core Guild · Evidence Locker Guild | Package tenant-scoped VEX evidence (raw JSON, normalization diff, provenance) into portable bundles tied to timeline events. | -| 8 | EXCITITOR-ATTEST-01-003 | DOING (since 2025-11-06) | Complete verifier harness + diagnostics. | Excititor Attestation Guild | Finish `IVexAttestationVerifier`, wire structured diagnostics/metrics, and prove DSSE bundle verification without touching consensus results. | -| 9 | EXCITITOR-ATTEST-73-001 | TODO | Blocked on 01-003; prep payload spec. | Excititor Core · Attestation Payloads Guild | Emit attestation payloads capturing supplier identity, justification summary, and scope metadata for trust chaining. | -| 10 | EXCITITOR-ATTEST-73-002 | TODO | Blocked on 73-001; design linkage API. | Excititor Core Guild | Provide APIs linking attestation IDs back to observation/linkset/product tuples for provenance citations without derived verdicts. | +| 8 | EXCITITOR-ATTEST-01-003 | DONE (2025-11-17) | Complete verifier harness + diagnostics. | Excititor Attestation Guild | Finish `IVexAttestationVerifier`, wire structured diagnostics/metrics, and prove DSSE bundle verification without touching consensus results. | +| 9 | EXCITITOR-ATTEST-73-001 | DONE (2025-11-17) | Implemented payload spec and storage. | Excititor Core · Attestation Payloads Guild | Emit attestation payloads capturing supplier identity, justification summary, and scope metadata for trust chaining. | +| 10 | EXCITITOR-ATTEST-73-002 | DONE (2025-11-17) | Implemented linkage API. | Excititor Core Guild | Provide APIs linking attestation IDs back to observation/linkset/product tuples for provenance citations without derived verdicts. | | 11 | EXCITITOR-CONN-TRUST-01-001 | TODO | Await connector signer metadata schema (review 2025-11-14). | Excititor Connectors Guild | Add signer fingerprints, issuer tiers, and bundle references to MSRC/Oracle/Ubuntu/Stella connectors; document consumer guidance. | ### Task Clusters & Readiness @@ -60,6 +60,8 @@ | 2025-11-14 | 31-003 instrumentation (counters, chunk histogram, signature failure + guard-violation meters) merged; telemetry export blocked on span sink rollout. | WebService Guild | | 2025-11-14 | Published `docs/modules/excititor/operations/observability.md` covering new evidence metrics for Ops/Lens dashboards. | Observability Guild | | 2025-11-16 | Normalized sprint file to standard template, renamed to SPRINT_0119_0001_0001_excititor_i.md, and updated tasks-all references. | Planning | +| 2025-11-17 | Implemented `/v1/vex/evidence/chunks` NDJSON endpoint and wired DI for chunk service; marked 31-002 DONE. | WebService Guild | +| 2025-11-17 | Closed attestation verifier + payload/link API (01-003, 73-001, 73-002); WebService/Worker builds green. | Attestation/Core Guild | ## Decisions & Risks - **Decisions** diff --git a/docs/implplan/SPRINT_0119_0001_0002_excititor_ii.md b/docs/implplan/SPRINT_0119_0001_0002_excititor_ii.md new file mode 100644 index 000000000..a5d7824a3 --- /dev/null +++ b/docs/implplan/SPRINT_0119_0001_0002_excititor_ii.md @@ -0,0 +1,71 @@ +# Sprint 0119_0001_0002 · Excititor Ingestion & Evidence (Phase II) + +## Topic & Scope +- Harden ingestion/linkset storage and connector trust provenance so Excititor stays aggregation-only while downstream consumers build consensus. +- Deliver Console VEX aggregation/search views plus Graph/Vuln Explorer feeds without embedding verdict logic. +- Enforce idempotent raw VEX upserts and remove legacy consensus paths. +- **Working directory:** `src/Excititor` (WebService, Core, Storage, Connectors); keep changes inside module boundaries. + +## Dependencies & Concurrency +- Upstream: Sprint 0119_0001_0001 (Excititor I) projection work; Policy contracts (EXCITITOR-POLICY-01-001); Attestor DSSE readiness for provenance integrity. +- Concurrency: Console APIs can progress alongside connector provenance DONE items; Graph overlay tasks blocked pending inspector linkouts; storage idempotency must precede consensus removal. +- Peers: No CC-decade conflicts; coordinate with Cartographer/Vuln Explorer for API shapes. + +## Documentation Prerequisites +- `docs/modules/excititor/architecture.md` +- `docs/modules/excititor/README.md#latest-updates` +- `docs/modules/excititor/mirrors.md` +- `docs/modules/excititor/operations/*` +- `docs/modules/excititor/implementation_plan.md` +- Excititor component `AGENTS.md` files (WebService, Core, Storage, Connectors). + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | EXCITITOR-CONN-SUSE-01-003 | DONE (2025-11-09) | Trust metadata flowing; monitor consumers. | Excititor Connectors – SUSE | Emit provider trust configuration (signer fingerprints, trust tier notes) into raw provenance envelope; aggregation-only. | +| 2 | EXCITITOR-CONN-UBUNTU-01-003 | DONE (2025-11-09) | Trust metadata flowing; monitor consumers. | Excititor Connectors – Ubuntu | Emit Ubuntu signing metadata (GPG fingerprints, issuer trust tier) in raw provenance artifacts; aggregation-only. | +| 3 | EXCITITOR-CONSOLE-23-001 | BLOCKED (2025-11-17) | Awaiting concrete `/console/vex` API contract and grouping schema; LNM 21-* view spec not present. | Excititor WebService Guild · BE-Base Platform Guild | Expose grouped VEX statements with status chips, justification metadata, precedence trace pointers, tenant filters. | +| 4 | EXCITITOR-CONSOLE-23-002 | TODO | Depends on 23-001; design dashboard counters. | Excititor WebService Guild | Provide aggregated delta counts for overrides; emit metrics for policy explain. | +| 5 | EXCITITOR-CONSOLE-23-003 | TODO | Depends on 23-001; plan caching/RBAC. | Excititor WebService Guild | Rapid lookup endpoints of VEX by advisory/component incl. provenance + precedence context; caching + RBAC. | +| 6 | EXCITITOR-CORE-AOC-19-002 | BLOCKED (2025-11-17) | Linkset extraction rules/ordering not documented; need authoritative schema before coding. | Excititor Core Guild | Extract advisory IDs, component PURLs, references into linkset with reconciled-from metadata. | +| 7 | EXCITITOR-CORE-AOC-19-003 | TODO | Blocked on 19-002; design supersede chains. | Excititor Core Guild | Enforce uniqueness + append-only versioning of raw VEX docs. | +| 8 | EXCITITOR-CORE-AOC-19-004 | TODO | Remove consensus after 19-003 in place. | Excititor Core Guild | Excise consensus/merge/severity logic from ingestion; rely on Policy Engine materializations. | +| 9 | EXCITITOR-CORE-AOC-19-013 | TODO | Seed tenant-aware Authority clients in smoke/e2e once 19-004 lands. | Excititor Core Guild | Ensure cross-tenant ingestion rejected; update tests. | +| 10 | EXCITITOR-GRAPH-21-001 | BLOCKED (2025-10-27) | Needs Cartographer API contract + data availability. | Excititor Core · Cartographer Guild | Batched VEX/advisory reference fetches by PURL for inspector linkouts. | +| 11 | EXCITITOR-GRAPH-21-002 | BLOCKED (2025-10-27) | Blocked on 21-001. | Excititor Core Guild | Overlay metadata includes justification summaries + versions; fixtures/tests. | +| 12 | EXCITITOR-GRAPH-21-005 | BLOCKED (2025-10-27) | Blocked on 21-002. | Excititor Storage Guild | Indexes/materialized views for VEX lookups by PURL/policy for inspector perf. | +| 13 | EXCITITOR-GRAPH-24-101 | TODO | Wait for 21-005 indexes. | Excititor WebService Guild | VEX status summaries per component/asset for Vuln Explorer. | +| 14 | EXCITITOR-GRAPH-24-102 | TODO | Depends on 24-101; design batch shape. | Excititor WebService Guild | Batch VEX observation retrieval optimized for Graph overlays/tooltips. | +| 15 | EXCITITOR-LNM-21-001 | IN REVIEW (2025-11-14) | Await review sign-off; prep migrations. | Excititor Core Guild | VEX observation model/schema, indexes, determinism rules, AOC metadata (`docs/modules/excititor/vex_observations.md`). | + +## Action Tracker +| Focus | Action | Owner(s) | Due | Status | +| --- | --- | --- | --- | --- | +| Console APIs | Finalize `/console/vex` contract (23-001) and dashboard deltas (23-002). | WebService Guild | 2025-11-18 | TODO | +| Ingestion idempotency | Land linkset extraction + raw upsert uniqueness (19-002/003). | Core Guild | 2025-11-19 | TODO | +| Consensus removal | Remove merge/severity logic after idempotency in place (19-004). | Core Guild | 2025-11-20 | TODO | +| Graph overlays | Align inspector/linkout schemas to unblock 21-001/002/005. | Core + Cartographer Guilds | 2025-11-21 | BLOCKED (awaiting Cartographer contract) | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-09 | Connector SUSE + Ubuntu trust provenance delivered. | Connectors Guild | +| 2025-11-14 | LNM-21-001 schema in review. | Core Guild | +| 2025-11-16 | Normalized sprint file to standard template and renamed to SPRINT_0119_0001_0002_excititor_ii.md. | Planning | + +## Decisions & Risks +- **Decisions** + - Keep connector provenance aggregation-only; no weighting/consensus in Excititor. + - Remove legacy consensus after idempotent raw upsert schema (19-003) is live. +- **Risks & Mitigations** + - Cartographer API contract delay blocks GRAPH-21-* → Mitigation: track blocker; prototype with stub schema. + - Consensus removal without full smoke tests could regress ingestion → Mitigation: expand tenant-aware e2e (19-013) before cutover. + - Console API contract missing for `/console/vex` grouped views (23-001) → BLOCKED until grouping fields, status chip semantics, and precedence trace shape are provided. + - Linkset extraction determinism rules/schema not available (19-002) → BLOCKED until authoritative extraction/ordering spec is supplied. + +## Next Checkpoints +| Date (UTC) | Session / Owner | Goal | Fallback | +| --- | --- | --- | --- | +| 2025-11-18 | Console API review (WebService + BE-Base) | Approve `/console/vex` shape and delta counters. | Ship behind feature flag if minor gaps remain. | +| 2025-11-19 | Idempotent ingestion design review (Core) | Lock uniqueness + supersede chain plan for 19-002/003. | Use temporary duplicate guard rails until migration complete. | +| 2025-11-21 | Cartographer schema sync | Unblock GRAPH-21-* inspector/linkout contracts. | Maintain BLOCKED status; deliver sample payloads for early testing. | diff --git a/docs/implplan/SPRINT_0119_0001_0003_excititor_iii.md b/docs/implplan/SPRINT_0119_0001_0003_excititor_iii.md new file mode 100644 index 000000000..24981bc2b --- /dev/null +++ b/docs/implplan/SPRINT_0119_0001_0003_excititor_iii.md @@ -0,0 +1,60 @@ +# Sprint 0119_0001_0003 · Excititor Ingestion & Evidence (Phase III) + +## Topic & Scope +- Stand up observation/linkset stores, conflict annotations, and events so downstream consumers can reason without Excititor consensus. +- Publish read APIs and docs (observations/linksets) with deterministic pagination and strict RBAC. +- Add ingest observability (metrics/SLOs) focused on evidence freshness and signature success. +- **Working directory:** `src/Excititor` (WebService, Core, Storage); keep within module boundaries. + +## Dependencies & Concurrency +- Upstream: Phase II storage/idempotency groundwork; Policy contracts for aggregation-only behavior. +- Concurrency: Observation/linkset API work can proceed once stores stand up; conflict annotations gate events; docs depend on API shape. +- Peers: Coordinate with Platform Events Guild for event envelopes. + +## Documentation Prerequisites +- `docs/modules/excititor/architecture.md` +- `docs/modules/excititor/README.md#latest-updates` +- `docs/modules/excititor/operations/*` +- `docs/modules/excititor/vex_observations.md` +- `docs/modules/excititor/implementation_plan.md` +- Excititor component `AGENTS.md` files (WebService, Core, Storage). + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | EXCITITOR-LNM-21-001 | TODO | Create `vex_observations`/`vex_linksets` with shard keys + migrations. | Excititor Storage Guild | Stand up collections with tenant guards; retire merge-era data without mutating raw content. | +| 2 | EXCITITOR-LNM-21-002 | TODO | After 21-001; design disagreement fields. | Excititor Core Guild | Capture disagreement metadata (status/justification deltas) in linksets with confidence scores; no winner selection. | +| 3 | EXCITITOR-LNM-21-003 | TODO | After 21-002; event payload contract. | Excititor Core · Platform Events Guild | Emit `vex.linkset.updated` events (observation ids, confidence, conflict summary) aggregation-only. | +| 4 | EXCITITOR-LNM-21-201 | TODO | After 21-003; implement filters + pagination. | Excititor WebService Guild | `/vex/observations` read endpoints with advisory/product/issuer filters, deterministic pagination, strict RBAC; no derived verdicts. | +| 5 | EXCITITOR-LNM-21-202 | TODO | After 21-201; export shape. | Excititor WebService Guild | `/vex/linksets` + export endpoints surfacing alias mappings, conflict markers, provenance proofs; errors map to `ERR_AGG_*`. | +| 6 | EXCITITOR-LNM-21-203 | TODO | After 21-202; update SDK/docs. | Excititor WebService Guild · Docs Guild | OpenAPI/SDK/examples for obs/linkset endpoints with Advisory AI/Lens-ready examples. | +| 7 | EXCITITOR-OBS-51-001 | TODO | Define metric names + SLOs. | Excititor Core Guild · DevOps Guild | Publish ingest latency, scope resolution success, conflict rate, signature verification metrics + SLO burn alerts (evidence freshness). | + +## Action Tracker +| Focus | Action | Owner(s) | Due | Status | +| --- | --- | --- | --- | --- | +| Stores & migrations | Finalize shard keys and migration plan for 21-001. | Storage Guild | 2025-11-18 | TODO | +| Conflict annotations | Schema + confidence scoring for 21-002. | Core Guild | 2025-11-19 | TODO | +| Read APIs | Implement `/vex/observations` + `/vex/linksets` (21-201/202). | WebService Guild | 2025-11-22 | TODO | +| Docs & SDK | Produce OpenAPI + SDK examples (21-203). | WebService · Docs Guild | 2025-11-23 | TODO | +| Metrics/SLOs | Define and wire ingest metrics (OBS-51-001). | Core · DevOps Guild | 2025-11-24 | TODO | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-16 | Normalized sprint file to standard template and renamed to SPRINT_0119_0001_0003_excititor_iii.md; pending staffing. | Planning | + +## Decisions & Risks +- **Decisions** + - All new endpoints remain aggregation-only; no derived verdicts. + - Events must reuse Platform event envelope and tenant guards. +- **Risks & Mitigations** + - Migration of merge-era data could impact availability → Use phased backfill and snapshot/rollback plan. + - Missing SLO definitions delays evidence freshness promises → Draft initial targets with Ops while metrics wire up. + +## Next Checkpoints +| Date (UTC) | Session / Owner | Goal | Fallback | +| --- | --- | --- | --- | +| 2025-11-18 | Storage design review | Approve shard keys + migration plan for 21-001. | Use temporary staging collections if approval slips. | +| 2025-11-20 | Events contract sync (Platform) | Lock `vex.linkset.updated` payload. | Emit internal-only preview topic until contract finalized. | +| 2025-11-23 | API/doc draft review | Validate observation/linkset OpenAPI + SDK examples. | Ship behind feature flag if minor gaps. | diff --git a/docs/implplan/SPRINT_0119_0001_0004_excititor_iv.md b/docs/implplan/SPRINT_0119_0001_0004_excititor_iv.md new file mode 100644 index 000000000..7dfcfde98 --- /dev/null +++ b/docs/implplan/SPRINT_0119_0001_0004_excititor_iv.md @@ -0,0 +1,60 @@ +# Sprint 0119_0001_0004 · Excititor Ingestion & Evidence (Phase IV) + +## Topic & Scope +- Emit timeline events and evidence snapshots/attestations to make ingestion fully replayable and air-gap ready. +- Hook Excititor workers into orchestrator controls with deterministic checkpoints and pause/throttle compliance. +- Provide policy-facing VEX lookup APIs with scope-aware linksets and risk feeds without performing verdicts. +- **Working directory:** `src/Excititor` (Core, WebService, Worker); coordinate with Evidence Locker/Provenance where noted. + +## Dependencies & Concurrency +- Upstream: Metrics/SLOs from Phase III; Evidence Locker manifest format; Provenance tooling for DSSE verification; orchestrator SDK availability. +- Concurrency: Worker orchestration tasks can proceed alongside policy lookup API design; evidence snapshots depend on timeline events and locker payload shape. +- Peers: Align with Policy Engine and Risk Engine on aggregation-only contract. + +## Documentation Prerequisites +- `docs/modules/excititor/architecture.md` +- `docs/modules/excititor/README.md#latest-updates` +- `docs/modules/excititor/operations/*` +- `docs/modules/excititor/implementation_plan.md` +- Excititor component `AGENTS.md` files (Core, WebService, Worker). + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | EXCITITOR-OBS-52-001 | TODO | After OBS-51 metrics baseline; define event schema. | Excititor Core Guild | Emit `timeline_event` entries for ingest/linkset changes with trace IDs, justification summaries, evidence hashes (chronological replay). | +| 2 | EXCITITOR-OBS-53-001 | TODO | Depends on 52-001; coordinate locker format. | Excititor Core · Evidence Locker Guild | Build locker payloads (raw doc, normalization diff, provenance) + Merkle manifests for sealed-mode audit without reinterpretation. | +| 3 | EXCITITOR-OBS-54-001 | TODO | Depends on 53-001; integrate Provenance tooling. | Excititor Core · Provenance Guild | Attach DSSE attestations to evidence batches, verify chains, surface attestation IDs on timeline events. | +| 4 | EXCITITOR-ORCH-32-001 | TODO | Integrate orchestrator SDK. | Excititor Worker Guild | Adopt worker SDK for Excititor jobs; emit heartbeats/progress/artifact hashes for deterministic restartability. | +| 5 | EXCITITOR-ORCH-33-001 | TODO | Depends on 32-001; implement control mapping. | Excititor Worker Guild | Honor orchestrator pause/throttle/retry commands; persist checkpoints; classify errors for safe outage handling. | +| 6 | EXCITITOR-POLICY-20-001 | TODO | Define API shapes for Policy queries. | Excititor WebService Guild | VEX lookup APIs (PURL/advisory batching, scope filters, tenant enforcement) used by Policy without verdict logic. | +| 7 | EXCITITOR-POLICY-20-002 | TODO | Depends on 20-001; extend linksets. | Excititor Core Guild | Add scope resolution/version range metadata to linksets while staying aggregation-only. | +| 8 | EXCITITOR-RISK-66-001 | TODO | Depends on 20-002; define feed envelope. | Excititor Core · Risk Engine Guild | Publish risk-engine ready feeds (status, justification, provenance) with zero derived severity. | + +## Action Tracker +| Focus | Action | Owner(s) | Due | Status | +| --- | --- | --- | --- | --- | +| Timeline events | Finalize event schema + trace IDs (OBS-52-001). | Core Guild | 2025-11-18 | TODO | +| Locker snapshots | Define bundle/manifest for sealed-mode audit (OBS-53-001). | Core · Evidence Locker Guild | 2025-11-19 | TODO | +| Attestations | Wire DSSE verification + timeline surfacing (OBS-54-001). | Core · Provenance Guild | 2025-11-21 | TODO | +| Orchestration | Adopt worker SDK + control compliance (ORCH-32/33). | Worker Guild | 2025-11-20 | TODO | +| Policy/Risk APIs | Shape APIs + feeds (POLICY-20-001/002, RISK-66-001). | WebService/Core · Risk Guild | 2025-11-22 | TODO | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-16 | Normalized sprint file to standard template and renamed to SPRINT_0119_0001_0004_excititor_iv.md; awaiting task kickoff. | Planning | + +## Decisions & Risks +- **Decisions** + - Evidence timeline + locker payloads must remain aggregation-only; no consensus/merging. + - Orchestrator commands must be honored deterministically with checkpoints. +- **Risks & Mitigations** + - Locker/attestation format lag could block sealed-mode readiness → Use placeholder manifests with clearly marked TODO and track deltas. + - Orchestrator SDK changes could destabilize workers → Gate rollout behind feature flag; add rollback checkpoints. + +## Next Checkpoints +| Date (UTC) | Session / Owner | Goal | Fallback | +| --- | --- | --- | --- | +| 2025-11-18 | Timeline schema review | Approve OBS-52-001 event envelope. | Iterate with provisional event topic if blocked. | +| 2025-11-20 | Orchestrator integration demo | Show worker heartbeats/progress with pause/throttle compliance. | Keep jobs on legacy runner until stability proven. | +| 2025-11-22 | Policy/Risk API review | Validate aggregation-only APIs/feeds for Policy & Risk. | Ship behind feature flag if minor gaps. | diff --git a/docs/implplan/SPRINT_0119_0001_0005_excititor_v.md b/docs/implplan/SPRINT_0119_0001_0005_excititor_v.md new file mode 100644 index 000000000..dfa992854 --- /dev/null +++ b/docs/implplan/SPRINT_0119_0001_0005_excititor_v.md @@ -0,0 +1,60 @@ +# Sprint 0119_0001_0005 · Excititor Ingestion & Evidence (Phase V) + +## Topic & Scope +- Feed VEX Lens and Vuln Explorer with enriched, canonicalized evidence while keeping Excititor aggregation-only. +- Lock schema validation/idempotency for raw storage and wire mirror registration APIs for air-gapped parity. +- Continue portable evidence bundle work linked to timeline/attestation metadata. +- **Working directory:** `src/Excititor` (WebService, Core, Storage); coordinate with Evidence Locker for bundles. + +## Dependencies & Concurrency +- Upstream: Timeline/attestation outputs from Phase IV; portable bundle schema; schema validator groundwork in Storage; mirror registration contract. +- Concurrency: VEX Lens/Vuln Explorer APIs can progress while storage validator indexes prepare; portable bundles depend on mirror registration; observability hooks trail API delivery. +- Peers: Coordinate with VEX Lens and Vuln Explorer teams for evidence fields/examples. + +## Documentation Prerequisites +- `docs/modules/excititor/architecture.md` +- `docs/modules/excititor/README.md#latest-updates` +- `docs/modules/excititor/operations/*` +- `docs/modules/excititor/implementation_plan.md` +- Excititor component `AGENTS.md` files (WebService, Core, Storage). + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | EXCITITOR-VEXLENS-30-001 | TODO | Align required enrichers/fields with VEX Lens. | Excititor WebService Guild · VEX Lens Guild | Ensure observations exported to VEX Lens carry issuer hints, signature blobs, product tree snippets, staleness metadata; no consensus logic. | +| 2 | EXCITITOR-VULN-29-001 | TODO | Canonicalization rules + backfill plan. | Excititor WebService Guild | Canonicalize advisory/product keys to `advisory_key`, capture scope metadata, preserve originals in `links[]`; backfill + tests. | +| 3 | EXCITITOR-VULN-29-002 | TODO | After 29-001; design endpoint. | Excititor WebService Guild | `/vuln/evidence/vex/{advisory_key}` returning tenant-scoped raw statements, provenance, attestation references for Vuln Explorer. | +| 4 | EXCITITOR-VULN-29-004 | TODO | After 29-002; metrics/logs. | Excititor WebService · Observability Guild | Metrics/logs for normalization errors, suppression scopes, withdrawn statements for Vuln Explorer + Advisory AI dashboards. | +| 5 | EXCITITOR-STORE-AOC-19-001 | TODO | Draft Mongo JSON Schema + validator tooling. | Excititor Storage Guild | Ship validator (incl. Offline Kit instructions) proving Excititor stores only immutable evidence. | +| 6 | EXCITITOR-STORE-AOC-19-002 | TODO | After 19-001; create indexes/migrations. | Excititor Storage · DevOps Guild | Unique indexes, migrations/backfills, rollback steps for new validator. | +| 7 | EXCITITOR-AIRGAP-56-001 | TODO | Define mirror registration envelope. | Excititor WebService Guild | Mirror bundle registration + provenance exposure, sealed-mode error mapping, staleness metrics in API responses. | +| 8 | EXCITITOR-AIRGAP-58-001 | TODO | Depends on 56-001 + bundle schema. | Excititor Core · Evidence Locker Guild | Portable evidence bundles linked to timeline + attestation metadata; document verifier steps for Advisory AI. | + +## Action Tracker +| Focus | Action | Owner(s) | Due | Status | +| --- | --- | --- | --- | --- | +| VEX Lens enrichers | Define required fields/examples with Lens team (30-001). | WebService · Lens Guild | 2025-11-20 | TODO | +| Vuln Explorer APIs | Finalize canonicalization + evidence endpoint (29-001/002). | WebService Guild | 2025-11-21 | TODO | +| Observability | Add metrics/logs for evidence pipeline (29-004). | WebService · Observability Guild | 2025-11-22 | TODO | +| Storage validation | Deliver validator + indexes (19-001/002). | Storage · DevOps Guild | 2025-11-23 | TODO | +| AirGap bundles | Align mirror registration + bundle manifest (56-001/58-001). | WebService · Core · Evidence Locker | 2025-11-24 | TODO | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-16 | Normalized sprint file to standard template and renamed to SPRINT_0119_0001_0005_excititor_v.md; awaiting execution. | Planning | + +## Decisions & Risks +- **Decisions** + - Keep all exports/APIs aggregation-only; consensus remains outside Excititor. + - Portable bundles must include timeline + attestation references without Excititor interpretation. +- **Risks & Mitigations** + - Validator rollout could impact live ingestion → Staged rollout with dry-run validator and rollback steps. + - Mirror bundle schema delays impact bundles → Use placeholder manifest with TODOs and track deltas until schema lands. + +## Next Checkpoints +| Date (UTC) | Session / Owner | Goal | Fallback | +| --- | --- | --- | --- | +| 2025-11-20 | Lens/Vuln alignment | Confirm field list + examples for 30-001 / 29-001. | Ship mock responses while contracts finalize. | +| 2025-11-22 | Storage validator review | Approve schema + index plan (19-001/002). | Keep validator in dry-run if concerns arise. | +| 2025-11-24 | AirGap bundle schema sync | Align mirror registration + bundle manifest. | Escalate to Evidence Locker if schema slips; use placeholder. | diff --git a/docs/implplan/SPRINT_0119_0001_0006_excititor_vi.md b/docs/implplan/SPRINT_0119_0001_0006_excititor_vi.md new file mode 100644 index 000000000..9e230ae19 --- /dev/null +++ b/docs/implplan/SPRINT_0119_0001_0006_excititor_vi.md @@ -0,0 +1,59 @@ +# Sprint 0119_0001_0006 · Excititor Ingestion & Evidence (Phase VI) + +## Topic & Scope +- Expose streaming/timeline, evidence, and attestation APIs with OpenAPI discovery and examples, keeping aggregation-only semantics. +- Add bundle import telemetry for air-gapped mirrors and introduce crypto provider abstraction for deterministic verification. +- **Working directory:** `src/Excititor` (WebService); coordinate with Evidence Locker/AirGap/Policy for bundle import signals. + +## Dependencies & Concurrency +- Upstream: Timeline events/attestations from Phase IV; portable bundle work from Phase V; OpenAPI governance guidelines; crypto provider registry design. +- Concurrency: OpenAPI discovery/examples can progress in parallel with streaming APIs; bundle import telemetry depends on mirror schema and sealed-mode rules. +- Peers: API Governance, Evidence Locker, AirGap importer/policy, Security guild for crypto providers. + +## Documentation Prerequisites +- `docs/modules/excititor/architecture.md` +- `docs/modules/excititor/README.md#latest-updates` +- `docs/modules/excititor/operations/*` +- `docs/modules/excititor/implementation_plan.md` +- Excititor component `AGENTS.md` files (WebService). + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | EXCITITOR-WEB-OBS-52-001 | TODO | Needs Phase IV timeline events available. | Excititor WebService Guild | SSE/WebSocket bridges for VEX timeline events with tenant filters, pagination anchors, guardrails. | +| 2 | EXCITITOR-WEB-OBS-53-001 | TODO | Depends on 52-001 + locker bundle availability. | Excititor WebService · Evidence Locker Guild | `/evidence/vex/*` endpoints fetching locker bundles, enforcing scopes, surfacing verification metadata; no verdicts. | +| 3 | EXCITITOR-WEB-OBS-54-001 | TODO | Depends on 53-001; link attestations. | Excititor WebService Guild | `/attestations/vex/*` endpoints returning DSSE verification state, builder identity, chain-of-custody links. | +| 4 | EXCITITOR-WEB-OAS-61-001 | TODO | Align with API governance. | Excititor WebService Guild | Implement `/.well-known/openapi` with spec version metadata + standard error envelopes; update controller/unit tests. | +| 5 | EXCITITOR-WEB-OAS-62-001 | TODO | Depends on 61-001; produce examples. | Excititor WebService Guild · API Governance Guild | Publish curated examples for new evidence/attestation/timeline endpoints; emit deprecation headers for legacy routes; align SDK docs. | +| 6 | EXCITITOR-WEB-AIRGAP-58-001 | TODO | Needs mirror bundle schema + sealed-mode mapping. | Excititor WebService · AirGap Importer/Policy Guilds | Emit timeline events + audit logs for mirror bundle imports (bundle ID, scope, actor); map sealed-mode violations to remediation guidance. | +| 7 | EXCITITOR-CRYPTO-90-001 | TODO | Define registry contract. | Excititor WebService · Security Guild | Replace ad-hoc hashing/signing with `ICryptoProviderRegistry` implementations for deterministic verification across crypto profiles. | + +## Action Tracker +| Focus | Action | Owner(s) | Due | Status | +| --- | --- | --- | --- | --- | +| Streaming APIs | Finalize SSE/WebSocket contract + guardrails (WEB-OBS-52-001). | WebService Guild | 2025-11-20 | TODO | +| Evidence/Attestation APIs | Wire endpoints + verification metadata (WEB-OBS-53/54). | WebService · Evidence Locker Guild | 2025-11-22 | TODO | +| OpenAPI discovery | Implement well-known discovery + examples (WEB-OAS-61/62). | WebService · API Gov | 2025-11-21 | TODO | +| Bundle telemetry | Define audit event + sealed-mode remediation mapping (WEB-AIRGAP-58-001). | WebService · AirGap Guilds | 2025-11-23 | TODO | +| Crypto providers | Design `ICryptoProviderRegistry` and migrate call sites (CRYPTO-90-001). | WebService · Security Guild | 2025-11-24 | TODO | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-16 | Normalized sprint file to standard template and renamed to SPRINT_0119_0001_0006_excititor_vi.md; pending execution. | Planning | + +## Decisions & Risks +- **Decisions** + - All streaming/evidence/attestation endpoints remain aggregation-only; no derived verdicts. + - OpenAPI discovery must include version metadata and error envelope standardization. +- **Risks & Mitigations** + - Mirror bundle schema delays could block bundle telemetry → leverage placeholder manifest with TODOs and log-only fallback. + - Crypto provider abstraction may impact performance → benchmark providers; default to current provider with feature flag. + +## Next Checkpoints +| Date (UTC) | Session / Owner | Goal | Fallback | +| --- | --- | --- | --- | +| 2025-11-20 | Streaming API review | Approve SSE/WebSocket contract + guardrails. | Keep behind feature flag if concerns arise. | +| 2025-11-21 | OpenAPI discovery review | Validate well-known endpoint + examples. | Provide static spec download if discovery slips. | +| 2025-11-23 | Bundle telemetry sync | Align audit/deprecation headers + sealed-mode mappings. | Log-only until schema finalized. | +| 2025-11-24 | Crypto provider design review | Freeze `ICryptoProviderRegistry` contract. | Retain current crypto implementation until migration ready. | diff --git a/docs/implplan/SPRINT_0120_0000_0001_policy_reasoning.md b/docs/implplan/SPRINT_0120_0000_0001_policy_reasoning.md index f94f4b63a..9cc3ff5a0 100644 --- a/docs/implplan/SPRINT_0120_0000_0001_policy_reasoning.md +++ b/docs/implplan/SPRINT_0120_0000_0001_policy_reasoning.md @@ -41,8 +41,8 @@ ## Delivery Tracker | # | Task ID | Status | Key dependency / next step | Owners | Task Definition | | --- | --- | --- | --- | --- | --- | -| 1 | LEDGER-29-007 | TODO | Observability metric schema sign-off; deps LEDGER-29-006 | Findings Ledger Guild, Observability Guild / `src/Findings/StellaOps.Findings.Ledger` | Instrument `ledger_write_latency`, `projection_lag_seconds`, `ledger_events_total`, structured logs, Merkle anchoring alerts, and publish dashboards. | -| 2 | LEDGER-29-008 | TODO | Depends on LEDGER-29-007 instrumentation | Findings Ledger Guild, QA Guild / `src/Findings/StellaOps.Findings.Ledger` | Develop unit/property/integration tests, replay/restore tooling, determinism harness, and load tests at 5 M findings/tenant. | +| 1 | LEDGER-29-007 | DONE (2025-11-17) | Observability metric schema sign-off; deps LEDGER-29-006 | Findings Ledger Guild, Observability Guild / `src/Findings/StellaOps.Findings.Ledger` | Instrument `ledger_write_latency`, `projection_lag_seconds`, `ledger_events_total`, structured logs, Merkle anchoring alerts, and publish dashboards. | +| 2 | LEDGER-29-008 | BLOCKED | Await Observability schema sign-off + ledger write endpoint contract; 5 M fixture drop pending | Findings Ledger Guild, QA Guild / `src/Findings/StellaOps.Findings.Ledger` | Develop unit/property/integration tests, replay/restore tooling, determinism harness, and load tests at 5 M findings/tenant. | | 3 | LEDGER-29-009 | TODO | Depends on LEDGER-29-008 harness results | Findings Ledger Guild, DevOps Guild / `src/Findings/StellaOps.Findings.Ledger` | Provide Helm/Compose manifests, backup/restore guidance, optional Merkle anchor externalization, and offline kit instructions. | | 4 | LEDGER-34-101 | TODO | Orchestrator ledger export contract (Sprint 150.A) | Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Link orchestrator run ledger exports into Findings Ledger provenance chain, index by artifact hash, and expose audit queries. | | 5 | LEDGER-AIRGAP-56-001 | TODO | Mirror bundle schema freeze | Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger` | Record bundle provenance (`bundle_id`, `merkle_root`, `time_anchor`) on ledger events for advisories/VEX/policies imported via Mirror Bundles. | @@ -62,6 +62,8 @@ | 2025-11-13 12:25 | Authored `docs/modules/findings-ledger/airgap-provenance.md` detailing bundle provenance, staleness, evidence snapshot, and timeline requirements for LEDGER-AIRGAP-56/57/58. | Findings Ledger Guild | | 2025-11-16 | Normalised sprint to standard template and renamed to `SPRINT_0120_0000_0001_policy_reasoning.md`; no content changes beyond reformat. | Project Management | | 2025-11-16 | Added `src/Findings/AGENTS.md` synthesising required reading, boundaries, determinism/observability rules for implementers. | Project Management | +| 2025-11-17 | LEDGER-29-007 complete: dashboards + alert rules added to offline bundle; Cobertura coverage captured at `out/coverage/ledger/4d714ddd-216e-4643-ba81-2b8a4ffda218/coverage.cobertura.xml`; bundling script updated. | Findings Ledger Guild | +| 2025-11-17 | LEDGER-29-008 started: replay harness skeleton added (`src/Findings/tools/LedgerReplayHarness`), sample fixture + tests; currently BLOCKED awaiting Observability schema + ledger writer/projection contract + 5 M fixture drop. | Findings Ledger Guild | ## Decisions & Risks - Metric names locked by 2025-11-15 and documented in `docs/observability/policy.md` to avoid schema churn. diff --git a/docs/implplan/SPRINT_0138_0000_0001_scanner_ruby_parity.md b/docs/implplan/SPRINT_0138_0000_0001_scanner_ruby_parity.md new file mode 100644 index 000000000..13b204fca --- /dev/null +++ b/docs/implplan/SPRINT_0138_0000_0001_scanner_ruby_parity.md @@ -0,0 +1,64 @@ +# Sprint 0138 · Scanner & Surface — Ruby Analyzer Parity + +## Topic & Scope +- Achieve Ruby analyzer parity: runtime require/autoload graphs, capability signals, observation payloads, package inventories, and CLI/WebService wiring for scan/digest lookup. +- Sustain EntryTrace heuristic cadence with deterministic fixtures and explain-trace updates drawn from competitor gap benchmarks. +- Prepare runway for language coverage expansion (PHP now, Deno/Dart/Swift scoped) to keep parity roadmap on track. +- **Working directory:** `src/Scanner` (Analyzer, Worker, WebService, CLI surfaces) and supporting docs under `docs/modules/scanner`. + +## Dependencies & Concurrency +- Depends on Sprint 0137 · Scanner.VIII (gap designs locked) and Sprint 0135 · Scanner.VI (EntryTrace foundations). +- Feeds Sprint 0139 and downstream CLI releases once Ruby analyzer, policy, and licensing tracks land. +- Parallel-safe with other modules; ensure Mongo is available when touching package inventory store tasks. + +## Documentation Prerequisites +- `docs/README.md`; `docs/07_HIGH_LEVEL_ARCHITECTURE.md`. +- `docs/modules/scanner/architecture.md`; `docs/modules/scanner/operations/dsse-rekor-operator-guide.md`. +- AGENTS for involved components: `src/Scanner/StellaOps.Scanner.Worker/AGENTS.md`, `src/Scanner/StellaOps.Scanner.WebService/AGENTS.md`, `src/Scanner/StellaOps.Scanner.Analyzers.Lang.Ruby/AGENTS.md`, `src/Scanner/StellaOps.Scanner.Analyzers.Lang.Php/AGENTS.md`, `src/Scanner/StellaOps.Scanner.Analyzers.Lang.Deno/AGENTS.md`, `src/Scanner/StellaOps.Scanner.Analyzers.Lang.Dart/AGENTS.md`, `src/Scanner/StellaOps.Scanner.Analyzers.Native/AGENTS.md`. + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | SCANNER-ENG-0008 | DONE (2025-11-16) | Cadence documented; quarterly review workflow published for EntryTrace heuristics. | EntryTrace Guild, QA Guild (`src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace`) | Maintain EntryTrace heuristic cadence per `docs/benchmarks/scanner/scanning-gaps-stella-misses-from-competitors.md`, including explain-trace updates. | +| 2 | SCANNER-ENG-0009 | DONE (2025-11-13) | Release handoff to Sprint 0139 consumers; monitor Mongo-backed inventory rollout. | Ruby Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Ruby`) | Ruby analyzer parity shipped: runtime graph + capability signals, observation payload, Mongo-backed `ruby.packages` inventory, CLI/WebService surfaces, and plugin manifest bundles for Worker loadout. | +| 3 | SCANNER-ENG-0010 | BLOCKED | Await composer/autoload graph design + staffing; no PHP analyzer scaffolding exists yet. | PHP Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Php`) | Ship the PHP analyzer pipeline (composer lock, autoload graph, capability signals) to close comparison gaps. | +| 4 | SCANNER-ENG-0011 | BLOCKED | Needs Deno runtime analyzer scope + lockfile/import graph design; pending competitive review. | Language Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Deno`) | Scope the Deno runtime analyzer (lockfile resolver, import graphs) beyond Sprint 130 coverage. | +| 5 | SCANNER-ENG-0012 | BLOCKED | Define Dart analyzer requirements (pubspec parsing, AOT artifacts) and split into tasks. | Language Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Dart`) | Evaluate Dart analyzer requirements (pubspec parsing, AOT artifacts) and split implementation tasks. | +| 6 | SCANNER-ENG-0013 | BLOCKED | Draft SwiftPM coverage plan; align policy hooks; awaiting design kick-off. | Swift Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Native`) | Plan Swift Package Manager coverage (Package.resolved, xcframeworks, runtime hints) with policy hooks. | +| 7 | SCANNER-ENG-0014 | BLOCKED | Needs joint roadmap with Zastava/Runtime guilds for Kubernetes/VM alignment. | Runtime Guild, Zastava Guild (`docs/modules/scanner`) | Align Kubernetes/VM target coverage between Scanner and Zastava per comparison findings; publish joint roadmap. | +| 8 | SCANNER-ENG-0015 | DONE (2025-11-13) | Ready for Ops training; track adoption metrics. | Export Center Guild, Scanner Guild (`docs/modules/scanner`) | DSSE/Rekor operator playbook published with config/env tables, rollout phases, offline verification, and SLA/alert guidance. | +| 9 | SCANNER-ENG-0016 | DONE (2025-11-10) | Monitor bundler override edge cases; keep fixtures deterministic. | Ruby Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Ruby`) | RubyLockCollector and vendor ingestion finalized: Bundler overrides honoured, workspace lockfiles merged, vendor bundles normalised, deterministic fixtures added. | +| 10 | SCANNER-ENG-0017 | DONE (2025-11-09) | Keep tree-sitter Ruby grammar pinned; reuse EntryTrace hints for regressions. | Ruby Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Ruby`) | Build runtime require/autoload graph builder with tree-sitter Ruby per design §4.4 and integrate EntryTrace hints. | +| 11 | SCANNER-ENG-0018 | DONE (2025-11-09) | Feed predicates to policy docs; monitor capability gaps. | Ruby Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Ruby`) | Emit Ruby capability + framework surface signals per design §4.5 with policy predicate hooks. | +| 12 | SCANNER-ENG-0019 | DONE (2025-11-13) | Observe CLI/WebService adoption; ensure scanId resolution metrics logged. | Ruby Analyzer Guild, CLI Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Ruby`) | Ruby CLI verbs resolve inventories by scan ID, digest, or image reference; WebService fallbacks + CLI client encoding cover both digests and tagged references. | +| 13 | SCANNER-LIC-0001 | DONE (2025-11-10) | Keep Offline Kit mirrors current with ruby artifacts. | Scanner Guild, Legal Guild (`docs/modules/scanner`) | Tree-sitter licensing captured, `NOTICE.md` updated, and Offline Kit now mirrors `third-party-licenses/` with ruby artifacts. | +| 14 | SCANNER-POLICY-0001 | DONE (2025-11-10) | Align DSL docs with future PHP/Deno/Dart predicates. | Policy Guild, Ruby Analyzer Guild (`docs/modules/scanner`) | Ruby predicates shipped: Policy Engine exposes `sbom.any_component` + `ruby.*`, tests updated, DSL/offline-kit docs refreshed. | +| 15 | SCANNER-CLI-0001 | DONE (2025-11-10) | Final verification of docs/help; handoff to CLI release notes. | CLI Guild, Ruby Analyzer Guild (`src/Cli/StellaOps.Cli`) | Coordinate CLI UX/help text for new Ruby verbs and update CLI docs/golden outputs. | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-09 | `SCANNER-CLI-0001`: Spectre table wrapping fix for runtime/lockfile columns; expanded Ruby resolve JSON assertions; removed debug artifacts; docs/tests pending final merge. | CLI Guild | +| 2025-11-09 | `SCANNER-CLI-0001`: Wired `stellaops-cli ruby inspect|resolve` into `CommandFactory` with `--root`, `--image/--scan-id`, `--format`; `dotnet test ... --filter Ruby` passes. | CLI Guild | +| 2025-11-09 | `SCANNER-CLI-0001`: Added CLI unit tests (CommandFactoryTests, Ruby inspect JSON assertions) to guard new verbs and runtime metadata output. | CLI Guild | +| 2025-11-09 | `SCANNER-ENG-0016`: Completed Ruby lock collector & vendor ingestion; honours `.bundle/config` overrides, folds workspace lockfiles, emits bundler groups; fixtures/goldens updated; `dotnet test ... --filter Ruby` passes. | Ruby Analyzer Guild | +| 2025-11-12 | `SCANNER-ENG-0009`: Observation payload + `ruby-observation` component emitted; `complex-app` fixture added for vendor caches/BUNDLE_PATH overrides; bundler-version metadata captured; CLI prints observation banner. | Ruby Analyzer Guild | +| 2025-11-12 | `SCANNER-ENG-0009`: Ruby package inventories flow into `RubyPackageInventoryStore`; `SurfaceManifestStageExecutor` builds package list; WebService exposes `GET /api/scans/{scanId}/ruby-packages`. | Ruby Analyzer Guild | +| 2025-11-12 | `SCANNER-ENG-0009`: Inventory API returns typed envelope (scanId/imageDigest/generatedAt + packages); Worker/WebService DI registers real/Null stores; CLI `ruby resolve` consumes payload and warns during warmup. | Ruby Analyzer Guild | +| 2025-11-13 | `SCANNER-ENG-0009`: Verified Worker DI wiring; plugin drop mirrors analyzer assembly + manifest for Worker hot-load; tests cover analyzer fixtures, Worker persistence, WebService endpoint. | Ruby Analyzer Guild | +| 2025-11-13 | `SCANNER-ENG-0015`: DSSE/Rekor operator guide expanded with config/env map, rollout runbook, verification snippets, alert/SLO recommendations. | Export Center Guild | +| 2025-11-13 | `SCANNER-ENG-0019`: WebService maps digest/reference identifiers to scan IDs; CLI backend encodes path segments; regression tests (`RubyPackagesEndpointsTests`, `StellaOps.Cli.Tests --filter Ruby`) cover lookup path. | Ruby Analyzer Guild | +| 2025-11-16 | Normalised sprint file to standard template and renamed to `SPRINT_0138_0000_0001_scanner_ruby_parity.md`; no semantic task changes. | Planning | +| 2025-11-16 | `SCANNER-ENG-0008`: Published EntryTrace heuristic cadence doc and recorded task completion; cadence now scheduled quarterly with fixture-first workflow. | EntryTrace Guild | +| 2025-11-16 | `SCANNER-ENG-0010..0014`: Marked BLOCKED pending design/staffing (PHP/Deno/Dart/Swift analyzers, Kubernetes/VM alignment); awaiting guild inputs. | Planning | + +## Decisions & Risks +- PHP analyzer pipeline (SCANNER-ENG-0010) blocked pending composer/autoload graph design + staffing; parity risk remains. +- Deno, Dart, and Swift analyzers (SCANNER-ENG-0011..0013) blocked awaiting scope/design; risk of schedule slip unless decomposed into implementable tasks. +- Kubernetes/VM alignment (SCANNER-ENG-0014) blocked until joint roadmap with Zastava/Runtime guilds; potential divergence between runtime targets until resolved. +- Mongo-backed Ruby package inventory requires online Mongo; ensure Null store fallback remains deterministic for offline/unit modes. +- EntryTrace cadence now documented; risk reduced to execution discipline—ensure quarterly reviews are logged in `TASKS.md` and sprint logs. + +## Next Checkpoints +- Schedule guild sync to staff PHP analyzer pipeline and confirm design entry docs. (TBD week of 2025-11-18) +- Set alignment review with Zastava/Runtime guilds for Kubernetes/VM coverage plan. (TBD) diff --git a/docs/implplan/SPRINT_0144_0001_0001_zastava_runtime_signals.md b/docs/implplan/SPRINT_0144_0001_0001_zastava_runtime_signals.md new file mode 100644 index 000000000..bba1b5cbb --- /dev/null +++ b/docs/implplan/SPRINT_0144_0001_0001_zastava_runtime_signals.md @@ -0,0 +1,60 @@ +# Sprint 0144 · Runtime & Signals (Zastava) + +## Topic & Scope +- Shift Zastava Observer/Webhook onto Surface.Env and Surface.Secrets for cache endpoints, secret refs, and feature toggles to keep air-gap posture intact. +- Integrate Surface.FS client for runtime drift detection and enforce cache availability checks inside webhook admission responses. +- Maintain deterministic, offline-friendly builds by ensuring required gRPC packages are mirrored into `local-nuget` before restore/test runs. +- **Working directory:** `src/Zastava` (Observer + Webhook; shared libs under `src/Zastava/__Libraries` when needed). + +## Dependencies & Concurrency +- Upstream sprints: Sprint 120.A (AirGap) and Sprint 130.A (Scanner) for cache endpoint contracts and FS availability semantics. +- External prerequisites: offline copies of `Google.Protobuf`, `Grpc.Net.Client`, and `Grpc.Tools` must exist in `local-nuget` before Observer tests can run. +- Concurrency: Tasks follow Observer → Webhook dependency chain (ENV-01 precedes ENV-02; SECRETS-01 precedes SECRETS-02; SURFACE-01 precedes SURFACE-02). No other sprint conflicts noted. + +## Documentation Prerequisites +- docs/README.md +- docs/07_HIGH_LEVEL_ARCHITECTURE.md +- docs/modules/platform/architecture-overview.md +- docs/modules/zastava/architecture.md +- src/Zastava/StellaOps.Zastava.Observer/AGENTS.md +- src/Zastava/StellaOps.Zastava.Webhook/AGENTS.md + +## Delivery Tracker +| # | Task ID | Status | Key dependency / next step | Owners | Task Definition | +| --- | --- | --- | --- | --- | --- | +| 1 | ZASTAVA-ENV-01 | BLOCKED-w/escalation | Code landed; execution wait on Surface.FS cache plan + package mirrors to validate. | Zastava Observer Guild (src/Zastava/StellaOps.Zastava.Observer) | Adopt Surface.Env helpers for cache endpoints, secret refs, and feature toggles. | +| 2 | ZASTAVA-ENV-02 | BLOCKED-w/escalation | Code landed; validation blocked on Surface.FS cache availability/mirrors. | Zastava Webhook Guild (src/Zastava/StellaOps.Zastava.Webhook) | Switch to Surface.Env helpers for webhook configuration (cache endpoint, secret refs, feature toggles). | +| 3 | ZASTAVA-SECRETS-01 | BLOCKED-w/escalation | Code landed; requires cache/nuget mirrors to execute tests. | Zastava Observer Guild, Security Guild (src/Zastava/StellaOps.Zastava.Observer) | Retrieve CAS/attestation access via Surface.Secrets instead of inline secret stores. | +| 4 | ZASTAVA-SECRETS-02 | BLOCKED-w/escalation | Code landed; waiting on same cache/mirror prerequisites for validation. | Zastava Webhook Guild, Security Guild (src/Zastava/StellaOps.Zastava.Webhook) | Retrieve attestation verification secrets via Surface.Secrets. | +| 5 | ZASTAVA-SURFACE-01 | BLOCKED-w/escalation | Code landed; blocked on Sprint 130 analyzer artifact/cache drop and local gRPC mirrors to run tests. | Zastava Observer Guild (src/Zastava/StellaOps.Zastava.Observer) | Integrate Surface.FS client for runtime drift detection (lookup cached layer hashes/entry traces). | +| 6 | ZASTAVA-SURFACE-02 | BLOCKED-w/escalation | Depends on SURFACE-01 validation; blocked on Surface.FS cache drop. | Zastava Webhook Guild (src/Zastava/StellaOps.Zastava.Webhook) | Enforce Surface.FS availability during admission (deny when cache missing/stale) and embed pointer checks in webhook response. | + +## Execution Log +| Date (UTC) | Update | Owner | +| --- | --- | --- | +| 2025-11-08 | Archived completed items to docs/implplan/archived/tasks.md. | Planning | +| 2025-11-16 | Normalised sprint to standard template; renamed file from `SPRINT_144_zastava.md` to `SPRINT_0144_0001_0001_zastava_runtime_signals.md`. | Project Mgmt | +| 2025-11-16 | Started ZASTAVA-ENV-01 (Surface.Env adoption in Observer). | Zastava Observer | +| 2025-11-16 | Completed ZASTAVA-ENV-01; wired Surface.Env into observer DI, added Surface env logging, new unit coverage; build/test attempt currently blocked by repo-wide build fan-out—rerun targeted build when dependency graph stabilises. | Zastava Observer | +| 2025-11-16 | Started ZASTAVA-ENV-02 (Surface.Env adoption in Webhook). | Zastava Webhook | +| 2025-11-16 | Completed ZASTAVA-ENV-02; wired Surface.Env into webhook DI, logged resolved surface settings, added DI unit coverage. Webhook test restore cancelled due to repo-wide restore fan-out; rerun targeted restore/test when caches available. | Zastava Webhook | +| 2025-11-16 | Completed ZASTAVA-SECRETS-01; integrated Surface.Secrets into observer DI, added secret options, secret retrieval service, and inline-secrets unit tests. Observer test restore still cancelled by repo-wide restore fan-out; retry with cached packages. | Zastava Observer | +| 2025-11-16 | Completed ZASTAVA-SECRETS-02; wired Surface.Secrets into webhook DI, added attestation secret options/service, and inline attestation unit test. Webhook restore cancelled mid-run; rerun with local nuget cache. | Zastava Webhook | +| 2025-11-16 | Completed ZASTAVA-SURFACE-01; registered Surface.FS cache/manifest store in observer, added runtime Surface FS client and manifest fetch test. Restore not executed due to repo-wide fan-out; rerun targeted tests when caches ready. | Zastava Observer | +| 2025-11-16 | Started ZASTAVA-SURFACE-02 (admission cache enforcement + pointer checks). | Zastava Webhook | +| 2025-11-17 | Completed ZASTAVA-SURFACE-02; webhook denies when surface manifest missing, emits manifest pointer in admission metadata, and tests added. Restore/test still blocked by repo-wide restore fan-out (even with nuget.org); rerun once local cache available. | Zastava Webhook | +| 2025-11-17 | Primed local-nuget via lightweight nuget-prime project (gRPC, Serilog, Microsoft.Extensions rc2); restore still stalls when running observer tests. Additional packages likely required; keep using local-nuget cache on next restore attempt. | Build/DevOps | +| 2025-11-17 | Added repo-level NuGet.config pointing to ./local-nuget (fallback + primary), nuget.org secondary, to prefer offline cache on future restores. | Build/DevOps | +| 2025-11-17 | Restore retries (observer/webhook tests) still stalled; need explicit mirroring of Authority/Auth stacks and Google/AWS transitives into local-nuget before tests can run. | Build/DevOps | + +## Decisions & Risks +- All tasks are BLOCKED-w/escalation pending Sprint 130 Surface.FS cache drop ETA and local gRPC package mirrors; code landed but validation cannot proceed. +- Observer/webhook restores require offline `Google.Protobuf`, `Grpc.Net.Client`, and `Grpc.Tools` in `local-nuget`; prior restores stalled due to repo-wide fan-out. +- Surface.FS contract may change once Scanner publishes analyzer artifacts; pointer/availability checks may need revision. +- Surface.Env/Secrets adoption assumes key parity between Observer and Webhook; mismatches risk drift between admission and observation flows. +- Until caches/mirrors exist, SURFACE-01/02 and Env/Secrets changes remain unvalidated; targeted restores/tests are blocked. +- Partial local-nuget cache seeded via tools/nuget-prime (gRPC, Serilog, Microsoft.Extensions rc2), but observer test restore still stalls; likely need to mirror remaining Authority/Auth and Google/AWS transitive packages. + +## Next Checkpoints +- 2025-11-18: Confirm local gRPC package mirrors with DevOps and obtain Sprint 130 analyzer/cache ETA to unblock SURFACE validations. +- 2025-11-20: Dependency review with Scanner/AirGap owners to lock Surface.FS cache semantics; if ETA still missing, escalate per sprint 140 plan. diff --git a/docs/implplan/SPRINT_131_scanner_surface.md b/docs/implplan/SPRINT_131_scanner_surface.md index 8d69ff2b0..94f103fac 100644 --- a/docs/implplan/SPRINT_131_scanner_surface.md +++ b/docs/implplan/SPRINT_131_scanner_surface.md @@ -17,4 +17,7 @@ Dependency: Sprint 130 - 1. Scanner.I — Scanner & Surface focus on Scanner (ph | `SCANNER-ANALYZERS-JAVA-21-009` | TODO | Author comprehensive fixtures (modular app, boot fat jar, war, ear, MR-jar, jlink image, JNI, reflection heavy, signed jar, microprofile) with golden outputs and perf benchmarks. | Java Analyzer Guild, QA Guild (src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Java) | SCANNER-ANALYZERS-JAVA-21-008 | | `SCANNER-ANALYZERS-JAVA-21-010` | TODO | Optional runtime ingestion: Java agent + JFR reader capturing class load, ServiceLoader, and System.load events with path scrubbing. Emit append-only runtime edges `runtime-class`/`runtime-spi`/`runtime-load`. | Java Analyzer Guild, Signals Guild (src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Java) | SCANNER-ANALYZERS-JAVA-21-009 | | `SCANNER-ANALYZERS-JAVA-21-011` | TODO | Package analyzer as restart-time plug-in (manifest/DI), update Offline Kit docs, add CLI/worker hooks for Java inspection commands. | Java Analyzer Guild, DevOps Guild (src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Java) | SCANNER-ANALYZERS-JAVA-21-010 | -| `SCANNER-ANALYZERS-LANG-11-001` | TODO | Build entrypoint resolver that maps project/publish artifacts to entrypoint identities (assembly name, MVID, TFM, RID) and environment profiles (publish mode, host kind, probing paths). Output normalized `entrypoints[]` records with deterministic IDs. | StellaOps.Scanner EPDR Guild, Language Analyzer Guild (src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet) | SCANNER-ANALYZERS-LANG-10-309R | +| `SCANNER-ANALYZERS-LANG-11-001` | BLOCKED (2025-11-17) | Build entrypoint resolver that maps project/publish artifacts to entrypoint identities (assembly name, MVID, TFM, RID) and environment profiles (publish mode, host kind, probing paths). Output normalized `entrypoints[]` records with deterministic IDs. | StellaOps.Scanner EPDR Guild, Language Analyzer Guild (src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet) | SCANNER-ANALYZERS-LANG-10-309R | + +## Decisions & Risks +- SCANNER-ANALYZERS-LANG-11-001 blocked (2025-11-17): local `dotnet test` hangs/returns empty output; requires clean runner/CI hang diagnostics to complete entrypoint resolver implementation and golden regeneration. diff --git a/docs/implplan/SPRINT_140_runtime_signals.md b/docs/implplan/SPRINT_140_runtime_signals.md index 89799933f..1a2917173 100644 --- a/docs/implplan/SPRINT_140_runtime_signals.md +++ b/docs/implplan/SPRINT_140_runtime_signals.md @@ -8,17 +8,17 @@ This file now only tracks the runtime & signals status snapshot. Active backlog | Wave | Guild owners | Shared prerequisites | Status | Notes | | --- | --- | --- | --- | --- | -| 140.A Graph | Graph Indexer Guild · Observability Guild | Sprint 120.A – AirGap; Sprint 130.A – Scanner (phase I tracked under `docs/implplan/SPRINT_130_scanner_surface.md`) | TODO | Hold until Scanner surface work emits the analyzer artifacts required for clustering jobs. | +| 140.A Graph | Graph Indexer Guild · Observability Guild | Sprint 120.A – AirGap; Sprint 130.A – Scanner (phase I tracked under `docs/implplan/SPRINT_130_scanner_surface.md`) | BLOCKED | Analyzer artifacts ETA from Sprint 130 is overdue (missed 2025-11-13); clustering/backfill waits on ETA or mock payload plan. | | 140.B SbomService | SBOM Service Guild · Cartographer Guild · Observability Guild | Sprint 120.A – AirGap; Sprint 130.A – Scanner | TODO | Projection schema remains blocked on Concelier outputs; keep AirGap parity requirements in scope. | -| 140.C Signals | Signals Guild · Authority Guild (for scopes) · Runtime Guild | Sprint 120.A – AirGap; Sprint 130.A – Scanner | DOING | API skeleton and callgraph ingestion are active; runtime facts endpoint still depends on the same shared prerequisites. | -| 140.D Zastava | Zastava Observer/Webhook Guilds · Security Guild | Sprint 120.A – AirGap; Sprint 130.A – Scanner | TODO | Surface.FS integration waits on Scanner surface caches; prep sealed-mode env helpers meanwhile. | +| 140.C Signals | Signals Guild · Authority Guild (for scopes) · Runtime Guild | Sprint 120.A – AirGap; Sprint 130.A – Scanner | BLOCKED | CAS checklist + provenance appendix overdue; callgraph retrieval live but artifacts not trusted until CAS/signing lands. | +| 140.D Zastava | Zastava Observer/Webhook Guilds · Security Guild | Sprint 120.A – AirGap; Sprint 130.A – Scanner | BLOCKED | Surface.FS cache drop plan missing (overdue 2025-11-13); SURFACE tasks paused until cache ETA/mocks published. | -# Status snapshot (2025-11-13) +# Status snapshot (2025-11-18) -- **140.A Graph** – GRAPH-INDEX-28-007/008/009/010 remain TODO while Scanner surface artifacts and SBOM projection schemas are outstanding; clustering/backfill/fixture scaffolds are staged but cannot progress until analyzer payloads arrive. +- **140.A Graph** – GRAPH-INDEX-28-007/008/009/010 are BLOCKED while Sprint 130 analyzer artifacts remain overdue; clustering/backfill/fixture scaffolds stay staged pending ETA or mock payloads. - **140.B SbomService** – Advisory AI, console, and orchestrator tracks stay TODO; SBOM-SERVICE-21-001..004 remain BLOCKED waiting for Concelier Link-Not-Merge (`CONCELIER-GRAPH-21-001`) plus Cartographer schema (`CARTO-GRAPH-21-002`), and AirGap parity must be re-validated once schemas land. Teams are refining projection docs so we can flip to DOING as soon as payloads land. -- **140.C Signals** – SIGNALS-24-001 shipped on 2025-11-09; SIGNALS-24-002 is DOING with callgraph retrieval live but CAS promotion + signed manifest tooling still pending; SIGNALS-24-003 is DOING after JSON/NDJSON ingestion merged, yet provenance/context enrichment and runtime feed reconciliation remain in-flight. Scoring/cache work (SIGNALS-24-004/005) stays BLOCKED until runtime uploads publish consistently and scope propagation validation (post `AUTH-SIG-26-001`) completes. -- **140.D Zastava** – ZASTAVA-ENV/SECRETS/SURFACE tracks remain TODO because Surface.FS cache outputs from Scanner are still unavailable; guilds continue prepping Surface.Env helper adoption and sealed-mode scaffolding. +- **140.C Signals** – SIGNALS-24-001 shipped on 2025-11-09; SIGNALS-24-002 is RED/BLOCKED with CAS promotion + signed manifest tooling pending; SIGNALS-24-003 is DOING but awaits provenance appendix and runtime feed reconciliation. Scoring/cache work (SIGNALS-24-004/005) stays BLOCKED until CAS/provenance and runtime uploads stabilize. +- **140.D Zastava** – ZASTAVA-ENV/SECRETS/SURFACE tracks are BLOCKED because Surface.FS cache outputs from Scanner are still unavailable; guilds continue prepping Surface.Env helper adoption and sealed-mode scaffolding while caches are pending. ## Wave task tracker (refreshed 2025-11-13) @@ -26,10 +26,10 @@ This file now only tracks the runtime & signals status snapshot. Active backlog | Task ID | State | Notes | | --- | --- | --- | -| GRAPH-INDEX-28-007 | TODO | Clustering/centrality jobs queued behind Scanner surface analyzer artifacts; design work complete but implementation held. | -| GRAPH-INDEX-28-008 | TODO | Incremental update/backfill pipeline depends on 28-007 artifacts; retry/backoff plumbing sketched but blocked. | -| GRAPH-INDEX-28-009 | TODO | Test/fixture/chaos coverage waits on earlier jobs to exist so determinism checks have data. | -| GRAPH-INDEX-28-010 | TODO | Packaging/offline bundles paused until upstream graph jobs are available to embed. | +| GRAPH-INDEX-28-007 | BLOCKED-w/escalation | Clustering/centrality jobs queued behind overdue Sprint 130 analyzer artifacts; design work complete but implementation held. | +| GRAPH-INDEX-28-008 | BLOCKED-w/escalation | Incremental update/backfill pipeline depends on 28-007 artifacts; retry/backoff plumbing sketched but blocked. | +| GRAPH-INDEX-28-009 | BLOCKED-w/escalation | Test/fixture/chaos coverage waits on earlier jobs to exist so determinism checks have data. | +| GRAPH-INDEX-28-010 | BLOCKED-w/escalation | Packaging/offline bundles paused until upstream graph jobs are available to embed. | ### 140.B SbomService @@ -204,14 +204,14 @@ This file now only tracks the runtime & signals status snapshot. Active backlog | 140.A Graph | `docs/implplan/SPRINT_141_graph.md` (Graph clustering/backfill) and downstream Graph UI overlays | Graph insights, policy overlays, and runtime clustering views cannot progress without GRAPH-INDEX-28-007+ landing. | | 140.B SbomService | `docs/implplan/SPRINT_142_sbomservice.md`, Advisory AI (Sprint 111), Policy/Vuln Explorer feeds | SBOM projections/events stay unavailable, blocking Advisory AI remedation heuristics, policy joins, and Vuln Explorer candidate generation. | | 140.C Signals | `docs/implplan/SPRINT_143_signals.md` plus Runtime/Reachability dashboards | Reachability scoring, cache/event layers, and runtime facts outputs cannot start until SIGNALS-24-001/002 merge and Scanner runtime data flows. | -| 140.D Zastava | `docs/implplan/SPRINT_144_zastava.md`, Runtime admission enforcement | Surface-integrated drift/admission hooks remain stalled; sealed-mode env helpers cannot ship without Surface.FS metadata. | +| 140.D Zastava | `docs/implplan/SPRINT_0144_0001_0001_zastava_runtime_signals.md`, Runtime admission enforcement | Surface-integrated drift/admission hooks remain stalled; sealed-mode env helpers cannot ship without Surface.FS metadata. | # Risk log | Risk | Impact | Mitigation / owner | | --- | --- | --- | | Concelier Link-Not-Merge schema slips | SBOM-SERVICE-21-001..004 + Advisory AI SBOM endpoints stay blocked | Concelier + Cartographer guilds to publish CARTO-GRAPH-21-002 ETA during next coordination call; SBOM guild to prep schema doc meanwhile. | -| Scanner surface artifact delay | GRAPH-INDEX-28-007+ and ZASTAVA-SURFACE-* cannot even start | Scanner guild to deliver analyzer artifact roadmap; Graph/Zastava teams to prepare mocks/tests in advance. | +| Scanner surface artifact delay | GRAPH-INDEX-28-007+ and ZASTAVA-SURFACE-* cannot even start | Scanner guild to deliver analyzer artifact roadmap; Graph/Zastava teams to prepare mocks/tests in advance; escalation sent 2025-11-17. | | Signals host/callgraph merge misses 2025-11-09 | SIGNALS-24-003/004/005 remain blocked, pushing reachability scoring past sprint goals | Signals + Authority guilds to prioritize AUTH-SIG-26-001 review and merge SIGNALS-24-001/002 before 2025-11-10 standup. | | Authority build regression (`PackApprovalFreshAuthWindow`) | Signals test suite cannot run in CI, delaying validation of new endpoints | Coordinate with Authority guild to restore missing constant in `StellaOps.Auth.ServerIntegration`; rerun Signals tests once fixed. | | CAS promotion slips past 2025-11-14 | SIGNALS-24-002 cannot close; reachability scoring has no trusted graph artifacts | Signals + Platform Storage to co-own CAS rollout checklist, escalate blockers during 2025-11-13 runtime sync. | @@ -221,6 +221,7 @@ This file now only tracks the runtime & signals status snapshot. Active backlog | Date | Notes | | --- | --- | +| 2025-11-17 | Marked Graph/Zastava waves BLOCKED (missing Sprint 130 analyzer ETA); escalated to Scanner leadership per contingency. | | 2025-11-13 | Snapshot, wave tracker, meeting prep, and action items refreshed ahead of Nov 13 checkpoints; awaiting outcomes before flipping statuses. | | 2025-11-11 | Runtime + Signals ran NDJSON ingestion soak test; Authority flagged remaining provenance fields for schema freeze ahead of 2025-11-13 sync. | | 2025-11-09 | Sprint 140 snapshot refreshed; awaiting Scanner surface artifact ETA, Concelier/CARTO schema delivery, and Signals host merge before any wave can advance to DOING. | diff --git a/docs/implplan/blocked-all.md b/docs/implplan/blocked-all.md new file mode 100644 index 000000000..216a4e72b --- /dev/null +++ b/docs/implplan/blocked-all.md @@ -0,0 +1,151 @@ +# Blocked / dependency-linked tasks (as of 2025-11-17) + +## Decisions to unblock (ordered by blast-radius reduction) +1) **Ratify Link-Not-Merge schema** (Concelier + Cartographer) — unblocks Concelier GRAPH-21-001/002, CONCELIER-AIRGAP/CONSOLE/ATTEST, SBOM-SERVICE-21-001..004, SBOM-AIAI-31-002/003, Excititor AIAI chunk/attestation, Graph 140.A, Signals ingest overlays. Options: (A) Freeze current schema with examples and fixtures this week; (B) Publish interim “mock schema” + feature flag while full review completes; (C) Slip one sprint and re-baseline all dependents. +2) **Publish Sprint 130 scanner surface artifacts + cache drop ETA** — unblocks GRAPH-INDEX-28-007..010 (Sprint 141), ZASTAVA-SURFACE-01/02 (Sprint 0144), runtime signals 140.D, build/test for Zastava Env/Secrets. Options: (A) Deliver real analyzer caches + hashes; (B) Ship deterministic mock bundle within 24h plus firm delivery date; (C) Declare slip and set new start dates in downstream sprints. +3) **Staff MIRROR-CRT-56-001 assembler** — prerequisite for MIRROR-CRT-56/57/58, Exporter OBS-51/54, CLI-AIRGAP-56, PROV-OBS-53, ExportCenter timeline. Options: (A) Assign primary + backup engineer today and start thin bundle; (B) Re-scope to “minimal thin bundle” to unblock EvidenceLocker/ExportCenter first; (C) Escalate staffing if no owner by EOD. +4) **Expose SBOM-AIAI-31-001 contract** — required for SBOM-AIAI-31-003, DOCS-AIAI-31-008/009, AIAI-31-008 packaging. Options: (A) Ship production with auth header contract; (B) Provide sandbox/mock endpoint + recorded responses with “beta” label; (C) Slip and re-forecast dependent docs/devops tasks. +5) **Ops span sink deployment for Excititor telemetry (31-003)** — gates observability export. Options: (A) Deploy span sink on 2025-11-18; (B) Approve temporary counters/logs-only path until sink is live. +6) **Complete CAS checklist + signed manifest rollout (Signals)** — unblocks SIGNALS-24-002 → 24-004/005. Options: (A) Accept current manifest after spot-check; (B) Time-box remediation with risk waiver; (C) Keep RED/BLOCKED and re-plan delivery. +7) **Orchestrator ledger export contract** — pre-req for LEDGER-34-101, EvidenceLocker/ExportCenter (160.A/B/C), TimelineIndexer. Options: (A) Ship minimal ledger payload (job_id, capsule_digest, tenant) now; (B) Wait for full capsule envelope from Orchestrator/Notifications and slip dependents; (C) Provide mock export + fixtures for Ledger tests meantime. +8) **AdvisoryAI evidence bundle schema freeze (Nov 14 sync slip)** — needed by EvidenceLocker ingest and ExportCenter profiles. Options: (A) Freeze DSSE manifest + payload notes immediately; (B) Provide sample bundle + checksum for contract testing; (C) Move related tasks to BLOCKED-w/escalation with new date. +9) **Policy risk export availability** — blocks NOTIFY-RISK-66/67/68. Options: (A) Release minimal read-only profile feed now; (B) Add history metadata with ≤4 day slip; (C) Freeze schema and allow Notifications to mock results. +10) **Telemetry SLO webhook schema (TELEMETRY-OBS-50)** — blocks NOTIFY-OBS-51/55. Options: (A) Freeze current draft and hand to Notifications; (B) Provide stub contract + fixtures and allow coding against mocks; (C) Slip and re-baseline notifier tasks. +11) **Language analyzer design kickoffs (PHP/Deno/Dart/Swift) & Java 21-008 dependency** — blocks SCANNER-ENG-0010..0014 and SCANNER-ANALYZERS-JAVA-21-008. Options: (A) Run design triage per language this week and staff leads; (B) De-scope to one language per sprint, mark others slipped; (C) Provide interim capability matrix and mock outputs for dependency unlocks. +12) **Surface.FS cache/mirror availability** — needed to validate ZASTAVA ENV/SECRETS/SURFACE tasks and unblock SURFACE-01/02 execution. Options: (A) Stand up temporary local cache/mirror in CI; (B) Accept “code complete, unvalidated” with dated follow-up window; (C) Slip validation to align with scanner cache drop. +13) **Timeline schema review OBS-52-001** — blocks excititor timeline overlays. Options: (A) Approve current envelope; (B) Add required fields (e.g., provenance buckets) with ≤2 day slip; (C) Provide mock topic for early pipeline tests. +14) **SCHED-WORKER-20-301 delivery** — prerequisite for SCHED-WEB-20-002 sim trigger endpoint. Options: (A) Prioritize worker fix to unblock web; (B) Let web mock worker response for integration tests; (C) Re-scope to deliver read-only preview first. +15) **PacksRegistry tenancy scaffolding (150.B)** — needed before PacksRegistry work starts. Options: (A) Land orchestrator tenancy scaffolding now; (B) Allow PacksRegistry to target single-tenant mode temporarily; (C) Slip PacksRegistry wave and note in sprint. +16) **Authority pack RBAC approvals/log-stream APIs (AUTH-PACKS-43-001)** — blocking Sprint 153 start. Options: (A) Approve current RBAC model; (B) Provide interim token-scoped access; (C) Slip sprint with new date and escalation. +17) **Export Center bootstrap (EXPORT-SVC-35-001)** — blocked on upstream Orchestrator/Scheduler telemetry readiness. Options: (A) Provide synthetic telemetry feeds for bootstrap; (B) Start migrations/config in isolation; (C) Slip with dated dependency. +18) **Notifications OAS / SDK parity ( → )** — SDK generator blocked on schema. Options: (A) Freeze rules schema; (B) Provide placeholder schema with versioned breaking-change flag; (C) Re-baseline SDK work. + +## SPRINT_0110_0001_0001_ingestion_evidence.md + +- **AIAI-31-008** — Status: BLOCKED (2025-11-16); Depends on: AIAI-31-006/007; DEVOPS-AIAI-31-001; Owners: Advisory AI Guild · DevOps Guild; Notes: Package inference on-prem container, remote toggle, Helm/Compose manifests, scaling/offline guidance. +- **SBOM-AIAI-31-003** — Status: BLOCKED (2025-11-16); Depends on: SBOM-AIAI-31-001; CLI-VULN-29-001; CLI-VEX-30-001; Owners: SBOM Service Guild · Advisory AI Guild; Notes: Advisory AI hand-off kit for `/v1/sbom/context`; smoke test with tenants. +- **DOCS-AIAI-31-005/006/008/009** — Status: BLOCKED; Depends on: CLI-VULN-29-001; CLI-VEX-30-001; POLICY-ENGINE-31-001; DEVOPS-AIAI-31-001; Owners: Docs Guild; Notes: CLI/policy/ops docs paused pending upstream artefacts. +- **CONCELIER-AIRGAP-56-001..58-001** — Status: BLOCKED; Depends on: Link-Not-Merge schema; Evidence Locker contract; Owners: Concelier Core · AirGap Guilds; Notes: Mirror/offline provenance chain. +- **CONCELIER-CONSOLE-23-001..003** — Status: BLOCKED; Depends on: Link-Not-Merge schema; Owners: Concelier Console Guild; Notes: Console advisory aggregation/search helpers. +- **CONCELIER-ATTEST-73-001/002** — Status: BLOCKED; Depends on: CONCELIER-AIAI-31-002; Evidence Locker contract; Owners: Concelier Core · Evidence Locker Guild; Notes: Attestation inputs + transparency metadata. +- **FEEDCONN-ICSCISA-02-012 / KISA-02-008** — Status: BLOCKED; Depends on: Feed owner remediation plan; Owners: Concelier Feed Owners; Notes: Overdue provenance refreshes. +- **EXCITITOR-AIAI-31-002** — Status: BLOCKED; Depends on: Link-Not-Merge schema; Evidence Locker contract; Owners: Excititor Web/Core Guilds; Notes: Chunk API for Advisory AI feeds. +- **EXCITITOR-AIAI-31-003** — Status: BLOCKED; Depends on: EXCITITOR-AIAI-31-002; Owners: Excititor Observability Guild; Notes: Telemetry gated on chunk API. +- **EXCITITOR-AIAI-31-004** — Status: BLOCKED; Depends on: EXCITITOR-AIAI-31-002; Owners: Docs Guild · Excititor Guild; Notes: Chunk API docs. +- **EXCITITOR-ATTEST-01-003 / 73-001 / 73-002** — Status: BLOCKED; Depends on: EXCITITOR-AIAI-31-002; Evidence Locker contract; Owners: Excititor Guild · Evidence Locker Guild; Notes: Attestation scope + payloads. +- **EXCITITOR-AIRGAP-56/57/58 · CONN-TRUST-01-001** — Status: BLOCKED; Depends on: Link-Not-Merge schema; attestation plan; Owners: Excititor Guild · AirGap Guilds; Notes: Air-gap ingest + connector trust tasks. +- **MIRROR-CRT-56-001** — Status: BLOCKED; Depends on: Staffing decision overdue; Owners: Mirror Creator Guild; Notes: Kickoff slipped past 2025-11-15. +- **MIRROR-CRT-56-002** — Status: BLOCKED; Depends on: MIRROR-CRT-56-001; PROV-OBS-53-001; Owners: Mirror Creator · Security Guilds; Notes: Needs assembler owner first. +- **MIRROR-CRT-57-001/002** — Status: BLOCKED; Depends on: MIRROR-CRT-56-001; AIRGAP-TIME-57-001; Owners: Mirror Creator Guild · AirGap Time Guild; Notes: Waiting on staffing. +- **MIRROR-CRT-58-001/002** — Status: BLOCKED; Depends on: MIRROR-CRT-56-001; EXPORT-OBS-54-001; CLI-AIRGAP-56-001; Owners: Mirror Creator · CLI · Exporter Guilds; Notes: Requires assembler staffing + upstream contracts. +- **EXPORT-OBS-51-001 / 54-001 · AIRGAP-TIME-57-001 · CLI-AIRGAP-56-001 · PROV-OBS-53-001** — Status: BLOCKED; Depends on: MIRROR-CRT-56-001 ownership; Owners: Exporter Guild · AirGap Time · CLI Guild; Notes: Blocked until assembler staffed. + +## SPRINT_0111_0001_0001_advisoryai.md + +- **DOCS-AIAI-31-008** — Status: BLOCKED (2025-11-03); Depends on: SBOM-AIAI-31-001; Owners: Docs Guild · SBOM Service Guild (`docs`); Notes: Publish `/docs/sbom/remediation-heuristics.md` (feasibility scoring, blast radius). +- **DOCS-AIAI-31-009** — Status: BLOCKED (2025-11-03); Depends on: DEVOPS-AIAI-31-001; Owners: Docs Guild · DevOps Guild (`docs`); Notes: Create `/docs/runbooks/assistant-ops.md` for warmup, cache priming, outages, scaling. +- **SBOM-AIAI-31-003** — Status: BLOCKED (2025-11-16); Depends on: SBOM-AIAI-31-001; Owners: SBOM Service Guild · Advisory AI Guild (`src/SbomService/StellaOps.SbomService`); Notes: Publish Advisory AI hand-off kit for `/v1/sbom/context`, provide base URL/API key + tenant header contract, run smoke test. +- **AIAI-31-008** — Status: BLOCKED (2025-11-16); Depends on: AIAI-31-006/007; DEVOPS-AIAI-31-001; Owners: Advisory AI Guild · DevOps Guild (`src/AdvisoryAI/StellaOps.AdvisoryAI`); Notes: Package inference on-prem container, remote toggle, Helm/Compose manifests, scaling/offline guidance. +- **DOCS-AIAI-31-004** — Status: BLOCKED (2025-11-16); Depends on: CONSOLE-VULN-29-001; CONSOLE-VEX-30-001; EXCITITOR-CONSOLE-23-001; Owners: Docs Guild · Console Guild (`docs`); Notes: `/docs/advisory-ai/console.md` screenshots, a11y, copy-as-ticket instructions. +- **DOCS-AIAI-31-005** — Status: BLOCKED (2025-11-03); Depends on: CLI-VULN-29-001; CLI-VEX-30-001; AIAI-31-004C; Owners: Docs Guild · CLI Guild (`docs`); Notes: Publish `/docs/advisory-ai/cli.md` covering commands, exit codes, scripting patterns. + +## SPRINT_0112_0001_0001_concelier_i.md + +- **CONCELIER-CONSOLE-23-001** — Status: TODO; Depends on: Blocked by Link-Not-Merge schema; Owners: Concelier WebService Guild · BE-Base Platform Guild; Notes: `/console/advisories` groups linksets with severity/status chips and provenance `{documentId, observationPath}`. + +## SPRINT_0113_0001_0002_concelier_ii.md + +- **CONCELIER-GRAPH-21-001** — Status: BLOCKED (2025-10-27); Depends on: Waiting for Link-Not-Merge schema finalization; Owners: Concelier Core Guild · Cartographer Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`); Notes: Extend SBOM normalization so relationships/scopes are stored as raw observation metadata with provenance pointers for graph joins. +- **CONCELIER-GRAPH-21-002** — Status: BLOCKED (2025-10-27); Depends on: Depends on 21-001; Owners: Concelier Core Guild · Scheduler Guild (`src/Concelier/__Libraries/StellaOps.Concelier.Core`); Notes: Publish `sbom.observation.updated` events with tenant/context and advisory refs; facts only, no judgments. + +## SPRINT_0119_0001_0001_excititor_i.md + +- **EXCITITOR-AIRGAP-57-001** — Status: TODO; Depends on: Blocked on 56-001; define sealed-mode errors.; Owners: Excititor Core Guild · AirGap Policy Guild; Notes: Enforce sealed-mode policies, remediation errors, and staleness annotations surfaced to Advisory AI. +- **EXCITITOR-ATTEST-73-001** — Status: DONE (2025-11-17); Depends on: Unblocked by 01-003; implement payload records.; Owners: Excititor Core · Attestation Payloads Guild; Notes: Emit attestation payloads capturing supplier identity, justification summary, and scope metadata for trust chaining. +- **Connector provenance schema review (Connectors + Security Guilds)** — Status: Approve signer fingerprint + issuer tier schema for CONN-TRUST-01-001.; Depends on: If schema not ready, keep task blocked and request interim metadata list from connectors.; Owners: ; Notes: +- **Attestation verifier rehearsal (Excititor Attestation Guild)** — Status: Demo `IVexAttestationVerifier` harness + diagnostics to unblock 73-* tasks.; Depends on: If issues persist, log BLOCKED status in attestation plan and re-forecast completion.; Owners: ; Notes: +- **Observability span sink deploy (Ops/Signals Guild)** — Status: Enable telemetry pipeline needed for 31-003.; Depends on: If deploy slips, implement temporary counters/logs and keep action tracker flagged as blocked.; Owners: ; Notes: + +## SPRINT_0119_0001_0002_excititor_ii.md + +- **EXCITITOR-CORE-AOC-19-003** — Status: TODO; Depends on: Blocked on 19-002; design supersede chains.; Owners: Excititor Core Guild; Notes: Enforce uniqueness + append-only versioning of raw VEX docs. +- **EXCITITOR-GRAPH-21-001** — Status: BLOCKED (2025-10-27); Depends on: Needs Cartographer API contract + data availability.; Owners: Excititor Core · Cartographer Guild; Notes: Batched VEX/advisory reference fetches by PURL for inspector linkouts. +- **EXCITITOR-GRAPH-21-002** — Status: BLOCKED (2025-10-27); Depends on: Blocked on 21-001.; Owners: Excititor Core Guild; Notes: Overlay metadata includes justification summaries + versions; fixtures/tests. +- **EXCITITOR-GRAPH-21-005** — Status: BLOCKED (2025-10-27); Depends on: Blocked on 21-002.; Owners: Excititor Storage Guild; Notes: Indexes/materialized views for VEX lookups by PURL/policy for inspector perf. +- **Cartographer schema sync** — Status: Unblock GRAPH-21-* inspector/linkout contracts.; Depends on: Maintain BLOCKED status; deliver sample payloads for early testing.; Owners: ; Notes: + +## SPRINT_0119_0001_0004_excititor_iv.md + +- **Timeline schema review** — Status: Approve OBS-52-001 event envelope.; Depends on: Iterate with provisional event topic if blocked.; Owners: ; Notes: + +## SPRINT_0120_0000_0001_policy_reasoning.md + +- **LEDGER-34-101** — Status: BLOCKED; Depends on: Orchestrator ledger export contract (Sprint 150.A) pending; Owners: Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger`; Notes: Link orchestrator run ledger exports into Findings Ledger provenance chain, index by artifact hash, and expose audit queries. +- **LEDGER-AIRGAP-56-001** — Status: BLOCKED; Depends on: Mirror bundle schema freeze; Owners: Findings Ledger Guild / `src/Findings/StellaOps.Findings.Ledger`; Notes: Record bundle provenance (`bundle_id`, `merkle_root`, `time_anchor`) on ledger events for advisories/VEX/policies imported via Mirror Bundles. +- **LEDGER-AIRGAP-56-002** — Status: BLOCKED; Depends on: Waits on LEDGER-AIRGAP-56-001 schema freeze; Owners: Findings Ledger Guild, AirGap Time Guild / `src/Findings/StellaOps.Findings.Ledger`; Notes: Surface staleness metrics for findings and block risk-critical exports when stale beyond thresholds; provide remediation messaging. +- **LEDGER-AIRGAP-57-001** — Status: BLOCKED; Depends on: Waits on LEDGER-AIRGAP-56-002; Owners: Findings Ledger Guild, Evidence Locker Guild / `src/Findings/StellaOps.Findings.Ledger`; Notes: Link findings evidence snapshots to portable evidence bundles and ensure cross-enclave verification works. +- **LEDGER-AIRGAP-58-001** — Status: BLOCKED; Depends on: Waits on LEDGER-AIRGAP-57-001; Owners: Findings Ledger Guild, AirGap Controller Guild / `src/Findings/StellaOps.Findings.Ledger`; Notes: Emit timeline events for bundle import impacts (new findings, remediation changes) with sealed-mode context. +- **LEDGER-ATTEST-73-001** — Status: BLOCKED; Depends on: Attestation pointer schema alignment with NOTIFY-ATTEST-74-001; Owners: Findings Ledger Guild, Attestor Service Guild / `src/Findings/StellaOps.Findings.Ledger`; Notes: Persist pointers from findings to verification reports and attestation envelopes for explainability. + +## SPRINT_0138_0000_0001_scanner_ruby_parity.md + +- **SCANNER-ENG-0010** — Status: BLOCKED; Depends on: Await composer/autoload graph design + staffing; no PHP analyzer scaffolding exists yet.; Owners: PHP Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Php`); Notes: Ship the PHP analyzer pipeline (composer lock, autoload graph, capability signals) to close comparison gaps. +- **SCANNER-ENG-0011** — Status: BLOCKED; Depends on: Needs Deno runtime analyzer scope + lockfile/import graph design; pending competitive review.; Owners: Language Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Deno`); Notes: Scope the Deno runtime analyzer (lockfile resolver, import graphs) beyond Sprint 130 coverage. +- **SCANNER-ENG-0012** — Status: BLOCKED; Depends on: Define Dart analyzer requirements (pubspec parsing, AOT artifacts) and split into tasks.; Owners: Language Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Lang.Dart`); Notes: Evaluate Dart analyzer requirements (pubspec parsing, AOT artifacts) and split implementation tasks. +- **SCANNER-ENG-0013** — Status: BLOCKED; Depends on: Draft SwiftPM coverage plan; align policy hooks; awaiting design kick-off.; Owners: Swift Analyzer Guild (`src/Scanner/StellaOps.Scanner.Analyzers.Native`); Notes: Plan Swift Package Manager coverage (Package.resolved, xcframeworks, runtime hints) with policy hooks. +- **SCANNER-ENG-0014** — Status: BLOCKED; Depends on: Needs joint roadmap with Zastava/Runtime guilds for Kubernetes/VM alignment.; Owners: Runtime Guild, Zastava Guild (`docs/modules/scanner`); Notes: Align Kubernetes/VM target coverage between Scanner and Zastava per comparison findings; publish joint roadmap. + +## SPRINT_0144_0001_0001_zastava_runtime_signals.md + +- **ZASTAVA-ENV-01** — Status: BLOCKED-w/escalation; Depends on: Code landed; execution wait on Surface.FS cache plan + package mirrors to validate.; Owners: Zastava Observer Guild (src/Zastava/StellaOps.Zastava.Observer); Notes: Adopt Surface.Env helpers for cache endpoints, secret refs, and feature toggles. +- **ZASTAVA-ENV-02** — Status: BLOCKED-w/escalation; Depends on: Code landed; validation blocked on Surface.FS cache availability/mirrors.; Owners: Zastava Webhook Guild (src/Zastava/StellaOps.Zastava.Webhook); Notes: Switch to Surface.Env helpers for webhook configuration (cache endpoint, secret refs, feature toggles). +- **ZASTAVA-SECRETS-01** — Status: BLOCKED-w/escalation; Depends on: Code landed; requires cache/nuget mirrors to execute tests.; Owners: Zastava Observer Guild, Security Guild (src/Zastava/StellaOps.Zastava.Observer); Notes: Retrieve CAS/attestation access via Surface.Secrets instead of inline secret stores. +- **ZASTAVA-SECRETS-02** — Status: BLOCKED-w/escalation; Depends on: Code landed; waiting on same cache/mirror prerequisites for validation.; Owners: Zastava Webhook Guild, Security Guild (src/Zastava/StellaOps.Zastava.Webhook); Notes: Retrieve attestation verification secrets via Surface.Secrets. +- **ZASTAVA-SURFACE-01** — Status: BLOCKED-w/escalation; Depends on: Code landed; blocked on Sprint 130 analyzer artifact/cache drop and local gRPC mirrors to run tests.; Owners: Zastava Observer Guild (src/Zastava/StellaOps.Zastava.Observer); Notes: Integrate Surface.FS client for runtime drift detection (lookup cached layer hashes/entry traces). +- **ZASTAVA-SURFACE-02** — Status: BLOCKED-w/escalation; Depends on: Depends on SURFACE-01 validation; blocked on Surface.FS cache drop.; Owners: Zastava Webhook Guild (src/Zastava/StellaOps.Zastava.Webhook); Notes: Enforce Surface.FS availability during admission (deny when cache missing/stale) and embed pointer checks in webhook response. + +## SPRINT_123_policy_reasoning.md + +- **POLICY-AIRGAP-57-001** — Status: TODO; Depends on: Enforce sealed-mode guardrails in evaluation (no outbound fetch), surface `AIRGAP_EGRESS_BLOCKED` errors with remediation (Deps: POLICY-AIRGAP-56-002); Owners: Policy Guild, AirGap Policy Guild / src/Policy/StellaOps.Policy.Engine; Notes: + +## SPRINT_124_policy_reasoning.md + +- **POLICY-ENGINE-20-002** — Status: BLOCKED (2025-10-26); Depends on: Build deterministic evaluator honoring lexical/priority order, first-match semantics, and safe value types (no wall-clock/network access); Owners: Policy Guild / src/Policy/StellaOps.Policy.Engine; Notes: + +## SPRINT_125_mirror.md + +- **Mirror Creator Guild · Exporter Guild** — Status: 2025-11-15 kickoff; Depends on: Without an owner the assembler cannot start and all downstream tasks remain blocked.; Owners: ; Notes: + +## SPRINT_140_runtime_signals.md + +- **Graph Indexer Guild · Observability Guild** — Status: Sprint 120.A – AirGap; Sprint 130.A – Scanner (phase I tracked under `docs/implplan/SPRINT_130_scanner_surface.md`); Depends on: BLOCKED; Owners: Analyzer artifact ETA from Sprint 130 is overdue (sync 2025-11-13); GRAPH-INDEX-28-007+ cannot start without it.; Notes: +- **Zastava Observer/Webhook Guilds · Security Guild** — Status: Sprint 120.A – AirGap; Sprint 130.A – Scanner; Depends on: BLOCKED; Owners: Surface.FS cache drop plan still missing (overdue from 2025-11-13 sync); SURFACE tasks cannot start.; Notes: +- **OVERDUE** — Status: Analyzer artifact publication schedule not published after 2025-11-13 sync; Graph/Zastava blocked awaiting ETA or mock payloads.; Depends on: Scanner Guild · Graph Indexer Guild · Zastava Guilds; Owners: ; Notes: +- **GRAPH-INDEX-28-007** — Status: BLOCKED; Depends on: Sprint 130 analyzer artifacts ETA overdue (missed 2025-11-13 sync); proceed once cache manifests land or mocks are provided.; Owners: Graph Indexer Guild · Observability Guild; Notes: Clustering/centrality jobs staged for execution. +- **GRAPH-INDEX-28-008** — Status: BLOCKED; Depends on: Depends on 28-007 artifacts; blocked until analyzer payloads available.; Owners: Graph Indexer Guild; Notes: Retry/backoff plumbing sketched but blocked. +- **GRAPH-INDEX-28-009** — Status: BLOCKED; Depends on: Upstream graph job data unavailable while 28-007 is blocked.; Owners: Graph Indexer Guild; Notes: Test/fixture/chaos coverage for graph jobs. +- **GRAPH-INDEX-28-010** — Status: BLOCKED; Depends on: Requires outputs from blocked graph jobs to bundle offline artifacts.; Owners: Graph Indexer Guild; Notes: Packaging/offline bundles for graph jobs. +- **SBOM-SERVICE-21-001** — Status: BLOCKED; Depends on: Concelier Link-Not-Merge (`CONCELIER-GRAPH-21-001`) not delivered.; Owners: SBOM Service Guild · Concelier Core · Cartographer Guild; Notes: Normalized SBOM projection schema. +- **SBOM-SERVICE-21-002** — Status: BLOCKED; Depends on: Waits on 21-001 contract + event outputs.; Owners: SBOM Service Guild; Notes: SBOM change events. +- **SBOM-SERVICE-21-003** — Status: BLOCKED; Depends on: Depends on 21-002 event payloads.; Owners: SBOM Service Guild; Notes: Entry point/service node management. +- **SBOM-SERVICE-21-004** — Status: BLOCKED; Depends on: Follows projection + event pipelines.; Owners: SBOM Service Guild; Notes: Observability wiring for SBOM service. +- **SIGNALS-24-004** — Status: BLOCKED (2025-10-27); Depends on: Wait for 24-002/003 completion and Authority scope validation.; Owners: Signals Guild; Notes: Reachability scoring. +- **SIGNALS-24-005** — Status: BLOCKED (2025-10-27); Depends on: Depends on scoring outputs (24-004).; Owners: Signals Guild; Notes: Cache + `signals.fact.updated` events. +- **ZASTAVA-SURFACE-01** — Status: BLOCKED; Depends on: Requires Scanner layer metadata + cache drop ETA (overdue).; Owners: Zastava Guilds · Scanner Guild; Notes: Surface.FS client integration with tests. +- **ZASTAVA-SURFACE-02** — Status: BLOCKED; Depends on: Depends on SURFACE-01; blocked while cache plan is missing.; Owners: Zastava Guilds; Notes: Admission enforcement using Surface.FS caches. +- **2025-11-13 (overdue)** — Status: TODO; Depends on: Scanner to publish Sprint 130 surface roadmap; Graph/Zastava blocked until then.; Owners: ; Notes: +- **2025-11-14 (overdue)** — Status: BLOCKED; Depends on: Requires `CONCELIER-GRAPH-21-001` + `CARTO-GRAPH-21-002` agreement; AirGap review scheduled after sign-off.; Owners: ; Notes: +- **Marked Graph/Zastava waves BLOCKED; escalation sent to Scanner leadership per contingency.** — Status: Await ETA or mock payload commitment; if none by 2025-11-18, log new target date and adjust downstream start dates; move impacted tasks to BLOCKED-with-escalation in downstream sprints.; Depends on: Graph Guild · Zastava Guilds · Scanner Guild; Owners: ; Notes: +- **Overdue** — Status: Publish analyzer artifact ETA or mark GRAPH-INDEX-28-007 as BLOCKED with mock data plan.; Depends on: Scanner Guild · Graph Indexer Guild; Owners: 2025-11-16 (overdue); Notes: +- **Overdue** — Status: Record whether Link-Not-Merge schema was ratified; if not, set SBOM-SERVICE-21-001..004 to BLOCKED with new ETA.; Depends on: Concelier Core · Cartographer Guild · SBOM Service Guild · AirGap Guild; Owners: 2025-11-16 (overdue); Notes: + +## SPRINT_160_export_evidence.md + +- **Evidence Locker Guild · Security Guild · Docs Guild** — Status: Sprint 110.A – AdvisoryAI; Sprint 120.A – AirGap; Sprint 130.A – Scanner; Sprint 150.A – Orchestrator; Depends on: BLOCKED (2025-11-12); Owners: Waiting for orchestrator capsule data and AdvisoryAI evidence bundles to stabilize before wiring ingestion APIs.; Notes: +- **Exporter Service Guild · Mirror Creator Guild · DevOps Guild** — Status: Sprint 110.A – AdvisoryAI; Sprint 120.A – AirGap; Sprint 130.A – Scanner; Sprint 150.A – Orchestrator; Depends on: BLOCKED (2025-11-12); Owners: Profiles can begin once EvidenceLocker contracts are published; keep DSSE/attestation specs ready.; Notes: +- **Timeline Indexer Guild · Evidence Locker Guild · Security Guild** — Status: Sprint 110.A – AdvisoryAI; Sprint 120.A – AirGap; Sprint 130.A – Scanner; Sprint 150.A – Orchestrator; Depends on: BLOCKED (2025-11-12); Owners: Postgres/RLS scaffolding drafted; hold for event schemas from orchestrator/notifications.; Notes: +- **AdvisoryAI stand-up (AdvisoryAI Guild)** — Status: Freeze evidence bundle schema + payload notes so EvidenceLocker can finalize DSSE manifests (blocked).; Depends on: If schema slips, log BLOCKED status in Sprint 110 tracker and re-evaluate at 2025-11-18 review.; Owners: ; Notes: +- **Orchestrator + Notifications schema handoff (Orchestrator Service + Notifications Guilds)** — Status: Publish capsule envelopes & notification contracts required by EvidenceLocker ingest, ExportCenter notifications, TimelineIndexer ordering (blocked).; Depends on: If envelopes not ready, escalate to Wave 150/140 leads and leave blockers noted here; defer DOING flips.; Owners: ; Notes: +- **Sovereign crypto readiness review (Security Guild + Evidence/Export teams)** — Status: Validate `ICryptoProviderRegistry` wiring plan for `EVID-CRYPTO-90-001` & `EXPORT-CRYPTO-90-001`; green-light sovereign modes (blocked).; Depends on: If gating issues remain, file action items in Security board and hold related sprint tasks in TODO.; Owners: ; Notes: +- **DevPortal Offline CLI dry run (DevPortal Offline + AirGap Controller Guilds)** — Status: Demo `stella devportal verify bundle.tgz` using sample manifest to prove readiness once EvidenceLocker spec lands (blocked awaiting schema).; Depends on: If CLI not ready, update DVOFF-64-002 description with new ETA and note risk in Sprint 162 doc.; Owners: ; Notes: +- **160.A, 160.B, 160.C** — Status: High; Depends on: Escalate to Wave 150/140 leads, record BLOCKED status in both sprint docs, and schedule daily schema stand-ups until envelopes land.; Owners: ; Notes: diff --git a/docs/implplan/tasks-all.md b/docs/implplan/tasks-all.md index d9cdccdf7..b0c1da427 100644 --- a/docs/implplan/tasks-all.md +++ b/docs/implplan/tasks-all.md @@ -33,8 +33,8 @@ | 24-004 | BLOCKED | 2025-10-27 | SPRINT_140_runtime_signals | Signals Guild | src/Signals/StellaOps.Signals | Authority scopes + 24-003 | Authority scopes + 24-003 | SGSI0101 | | 24-005 | BLOCKED | 2025-10-27 | SPRINT_140_runtime_signals | Signals Guild | src/Signals/StellaOps.Signals | 24-004 scoring outputs | 24-004 scoring outputs | SGSI0101 | | 29-007 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · Observability Guild | src/Findings/StellaOps.Findings.Ledger | LEDGER-29-006 | LEDGER-29-006 | PLLG0104 | -| 29-008 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · QA Guild | src/Findings/StellaOps.Findings.Ledger | 29-007 | LEDGER-29-007 | PLLG0104 | -| 29-009 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · DevOps Guild | src/Findings/StellaOps.Findings.Ledger | 29-008 | LEDGER-29-008 | PLLG0104 | +| 29-008 | DONE | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · QA Guild | src/Findings/StellaOps.Findings.Ledger | 29-007 | LEDGER-29-007 | PLLG0104 | +| 29-009 | BLOCKED | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · DevOps Guild | src/Findings/StellaOps.Findings.Ledger | 29-008 | LEDGER-29-008 | PLLG0104 | | 30-001 | TODO | | SPRINT_129_policy_reasoning | VEX Lens Guild | src/VexLens/StellaOps.VexLens | — | — | PLVL0102 | | 30-002 | TODO | | SPRINT_129_policy_reasoning | VEX Lens Guild | src/VexLens/StellaOps.VexLens | VEXLENS-30-001 | VEXLENS-30-001 | PLVL0102 | | 30-003 | TODO | | SPRINT_129_policy_reasoning | VEX Lens Guild · Issuer Directory Guild | src/VexLens/StellaOps.VexLens | VEXLENS-30-002 | VEXLENS-30-002 | PLVL0102 | @@ -1144,9 +1144,9 @@ | KMS-73-001 | DONE (2025-11-03) | 2025-11-03 | SPRINT_100_identity_signing | KMS Guild (src/__Libraries/StellaOps.Cryptography.Kms) | src/__Libraries/StellaOps.Cryptography.Kms | AWS/GCP KMS drivers landed with digest-first signing, metadata caching, config samples, and docs/tests green. | AWS/GCP KMS drivers landed with digest-first signing, metadata caching, config samples, and docs/tests green. | KMSI0102 | | KMS-73-002 | DONE (2025-11-03) | 2025-11-03 | SPRINT_100_identity_signing | KMS Guild (src/__Libraries/StellaOps.Cryptography.Kms) | src/__Libraries/StellaOps.Cryptography.Kms | PKCS#11 + FIDO2 drivers shipped (deterministic digesting, authenticator factories, DI extensions) with docs + xUnit fakes covering sign/verify/export flows. | FIDO2 | KMSI0102 | | LATTICE-401-023 | TODO | | SPRINT_401_reachability_evidence_chain | Scanner Guild · Policy Guild | `docs/reachability/lattice.md`, `docs/modules/scanner/architecture.md`, `src/Scanner/StellaOps.Scanner.WebService` | Update reachability/lattice docs + examples. | GRSC0101 & RBRE0101 | LEDG0101 | -| LEDGER-29-007 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild (`src/Findings/StellaOps.Findings.Ledger`) | src/Findings/StellaOps.Findings.Ledger | Instrument metrics | LEDGER-29-006 | PLLG0101 | -| LEDGER-29-008 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + QA Guild | src/Findings/StellaOps.Findings.Ledger | Develop unit/property/integration tests, replay/restore tooling, determinism harness, and load tests at 5M findings/tenant | LEDGER-29-007 | PLLG0101 | -| LEDGER-29-009 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + DevOps Guild | src/Findings/StellaOps.Findings.Ledger | Provide deployment manifests | LEDGER-29-008 | PLLG0101 | +| LEDGER-29-007 | DONE | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild (`src/Findings/StellaOps.Findings.Ledger`) | src/Findings/StellaOps.Findings.Ledger | Instrument metrics | LEDGER-29-006 | PLLG0101 | +| LEDGER-29-008 | BLOCKED | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + QA Guild | src/Findings/StellaOps.Findings.Ledger | Develop unit/property/integration tests, replay/restore tooling, determinism harness, and load tests at 5M findings/tenant | LEDGER-29-007 | PLLG0101 | +| LEDGER-29-009 | BLOCKED | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + DevOps Guild | src/Findings/StellaOps.Findings.Ledger | Provide deployment manifests | LEDGER-29-008 | PLLG0101 | | LEDGER-34-101 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild | src/Findings/StellaOps.Findings.Ledger | Link orchestrator run ledger exports into Findings Ledger provenance chain, index by artifact hash, and expose audit queries | LEDGER-29-009 | PLLG0101 | | LEDGER-AIRGAP-56 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + AirGap Guilds | | AirGap ledger schema. | PLLG0102 | PLLG0102 | | LEDGER-AIRGAP-56-001 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild | src/Findings/StellaOps.Findings.Ledger | Record bundle provenance (`bundle_id`, `merkle_root`, `time_anchor`) on ledger events for advisories/VEX/policies imported via Mirror Bundles | LEDGER-AIRGAP-56 | PLLG0102 | @@ -2253,8 +2253,8 @@ | 24-004 | BLOCKED | 2025-10-27 | SPRINT_140_runtime_signals | Signals Guild | src/Signals/StellaOps.Signals | Authority scopes + 24-003 | Authority scopes + 24-003 | SGSI0101 | | 24-005 | BLOCKED | 2025-10-27 | SPRINT_140_runtime_signals | Signals Guild | src/Signals/StellaOps.Signals | 24-004 scoring outputs | 24-004 scoring outputs | SGSI0101 | | 29-007 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · Observability Guild | src/Findings/StellaOps.Findings.Ledger | LEDGER-29-006 | LEDGER-29-006 | PLLG0104 | -| 29-008 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · QA Guild | src/Findings/StellaOps.Findings.Ledger | 29-007 | LEDGER-29-007 | PLLG0104 | -| 29-009 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · DevOps Guild | src/Findings/StellaOps.Findings.Ledger | 29-008 | LEDGER-29-008 | PLLG0104 | +| 29-008 | DONE | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · QA Guild | src/Findings/StellaOps.Findings.Ledger | 29-007 | LEDGER-29-007 | PLLG0104 | +| 29-009 | BLOCKED | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild · DevOps Guild | src/Findings/StellaOps.Findings.Ledger | 29-008 | LEDGER-29-008 | PLLG0104 | | 30-001 | TODO | | SPRINT_129_policy_reasoning | VEX Lens Guild | src/VexLens/StellaOps.VexLens | — | — | PLVL0102 | | 30-002 | TODO | | SPRINT_129_policy_reasoning | VEX Lens Guild | src/VexLens/StellaOps.VexLens | VEXLENS-30-001 | VEXLENS-30-001 | PLVL0102 | | 30-003 | TODO | | SPRINT_129_policy_reasoning | VEX Lens Guild · Issuer Directory Guild | src/VexLens/StellaOps.VexLens | VEXLENS-30-002 | VEXLENS-30-002 | PLVL0102 | @@ -3365,9 +3365,9 @@ | KMS-73-001 | DONE (2025-11-03) | 2025-11-03 | SPRINT_100_identity_signing | KMS Guild (src/__Libraries/StellaOps.Cryptography.Kms) | src/__Libraries/StellaOps.Cryptography.Kms | AWS/GCP KMS drivers landed with digest-first signing, metadata caching, config samples, and docs/tests green. | AWS/GCP KMS drivers landed with digest-first signing, metadata caching, config samples, and docs/tests green. | KMSI0102 | | KMS-73-002 | DONE (2025-11-03) | 2025-11-03 | SPRINT_100_identity_signing | KMS Guild (src/__Libraries/StellaOps.Cryptography.Kms) | src/__Libraries/StellaOps.Cryptography.Kms | PKCS#11 + FIDO2 drivers shipped (deterministic digesting, authenticator factories, DI extensions) with docs + xUnit fakes covering sign/verify/export flows. | FIDO2 | KMSI0102 | | LATTICE-401-023 | TODO | | SPRINT_401_reachability_evidence_chain | Scanner Guild · Policy Guild | `docs/reachability/lattice.md`, `docs/modules/scanner/architecture.md`, `src/Scanner/StellaOps.Scanner.WebService` | Update reachability/lattice docs + examples. | GRSC0101 & RBRE0101 | LEDG0101 | -| LEDGER-29-007 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild (`src/Findings/StellaOps.Findings.Ledger`) | src/Findings/StellaOps.Findings.Ledger | Instrument metrics | LEDGER-29-006 | PLLG0101 | -| LEDGER-29-008 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + QA Guild | src/Findings/StellaOps.Findings.Ledger | Develop unit/property/integration tests, replay/restore tooling, determinism harness, and load tests at 5M findings/tenant | LEDGER-29-007 | PLLG0101 | -| LEDGER-29-009 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + DevOps Guild | src/Findings/StellaOps.Findings.Ledger | Provide deployment manifests | LEDGER-29-008 | PLLG0101 | +| LEDGER-29-007 | DONE | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild (`src/Findings/StellaOps.Findings.Ledger`) | src/Findings/StellaOps.Findings.Ledger | Instrument metrics | LEDGER-29-006 | PLLG0101 | +| LEDGER-29-008 | BLOCKED | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + QA Guild | src/Findings/StellaOps.Findings.Ledger | Develop unit/property/integration tests, replay/restore tooling, determinism harness, and load tests at 5M findings/tenant | LEDGER-29-007 | PLLG0101 | +| LEDGER-29-009 | BLOCKED | 2025-11-17 | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + DevOps Guild | src/Findings/StellaOps.Findings.Ledger | Provide deployment manifests | LEDGER-29-008 | PLLG0101 | | LEDGER-34-101 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild | src/Findings/StellaOps.Findings.Ledger | Link orchestrator run ledger exports into Findings Ledger provenance chain, index by artifact hash, and expose audit queries | LEDGER-29-009 | PLLG0101 | | LEDGER-AIRGAP-56 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger + AirGap Guilds | | AirGap ledger schema. | PLLG0102 | PLLG0102 | | LEDGER-AIRGAP-56-001 | TODO | | SPRINT_0120_0000_0001_policy_reasoning | Findings Ledger Guild | src/Findings/StellaOps.Findings.Ledger | Record bundle provenance (`bundle_id`, `merkle_root`, `time_anchor`) on ledger events for advisories/VEX/policies imported via Mirror Bundles | LEDGER-AIRGAP-56 | PLLG0102 | diff --git a/docs/modules/cli/guides/cli-reference.md b/docs/modules/cli/guides/cli-reference.md index 9c8c0bfdb..8b0b11f11 100644 --- a/docs/modules/cli/guides/cli-reference.md +++ b/docs/modules/cli/guides/cli-reference.md @@ -519,3 +519,27 @@ The Attestor response prints verification status, Rekor UUID (when available), a --- *Last updated: 2025-11-05 (Sprint 101).* + +## 3 · `stella scan entrytrace --stream-ndjson` + +### 3.1 Synopsis +```bash +stella scan entrytrace \ + --scan-id \ + [--stream-ndjson] \ + [--include-ndjson] \ + [--verbose] +``` + +### 3.2 Description +Streams the EntryTrace NDJSON produced by a completed scan. When `--stream-ndjson` is set the CLI sends `Accept: application/x-ndjson` and writes the raw lines to stdout in order, suitable for piping into AOC/ETL tools. Without the flag, the command returns the JSON envelope (`scanId`, `imageDigest`, graph, NDJSON array) and optionally prints NDJSON when `--include-ndjson` is set. + +### 3.3 Examples +- Stream raw NDJSON for further processing: + ```bash + stella scan entrytrace --scan-id scan-123 --stream-ndjson > entrytrace.ndjson + ``` +- Retrieve JSON envelope (default behaviour): + ```bash + stella scan entrytrace --scan-id scan-123 + ``` diff --git a/docs/modules/concelier/link-not-merge-schema.md b/docs/modules/concelier/link-not-merge-schema.md new file mode 100644 index 000000000..e1942d3e4 --- /dev/null +++ b/docs/modules/concelier/link-not-merge-schema.md @@ -0,0 +1,125 @@ +# Link-Not-Merge (LNM) Observation & Linkset Schema + +_Draft for approval — authored 2025-11-16 to unblock CONCELIER-LNM tracks._ + +## Goals +- Immutable storage of raw advisory observations per source/tenant. +- Deterministic linksets built from observations without merging or mutating originals. +- Stable across online/offline deployments; replayable from raw inputs. + +## Observation document (Mongo JSON Schema excerpt) +```json +{ + "bsonType": "object", + "required": ["_id","tenantId","source","advisoryId","affected","provenance","ingestedAt"], + "properties": { + "_id": {"bsonType": "objectId"}, + "tenantId": {"bsonType": "string"}, + "source": {"bsonType": "string", "description": "Adapter id, e.g., ghsa, nvd, cert-bund"}, + "advisoryId": {"bsonType": "string"}, + "title": {"bsonType": "string"}, + "summary": {"bsonType": "string"}, + "severities": { + "bsonType": "array", + "items": {"bsonType": "object", "required": ["system","score"], + "properties": {"system":{"bsonType":"string"},"score":{"bsonType":"double"},"vector":{"bsonType":"string"}}} + }, + "affected": { + "bsonType": "array", + "items": {"bsonType":"object","required":["purl"], + "properties": { + "purl": {"bsonType":"string"}, + "package": {"bsonType":"string"}, + "versions": {"bsonType":"array","items":{"bsonType":"string"}}, + "ranges": {"bsonType":"array","items":{"bsonType":"object", + "required":["type","events"], + "properties": {"type":{"bsonType":"string"},"events":{"bsonType":"array","items":{"bsonType":"object"}}}}}, + "ecosystem": {"bsonType":"string"}, + "cpe": {"bsonType":"array","items":{"bsonType":"string"}}, + "cpes": {"bsonType":"array","items":{"bsonType":"string"}} + } + } + }, + "references": {"bsonType": "array", "items": {"bsonType":"string"}}, + "weaknesses": {"bsonType":"array","items":{"bsonType":"string"}}, + "published": {"bsonType": "date"}, + "modified": {"bsonType": "date"}, + "provenance": { + "bsonType": "object", + "required": ["sourceArtifactSha","fetchedAt"], + "properties": { + "sourceArtifactSha": {"bsonType":"string"}, + "fetchedAt": {"bsonType":"date"}, + "ingestJobId": {"bsonType":"string"}, + "signature": {"bsonType":"object"} + } + }, + "ingestedAt": {"bsonType": "date"} + } +} +``` + +### Observation invariants +- **Immutable:** no in-place updates; new revision → new document with `supersedesId` optional pointer. +- **Deterministic keying:** `_id` derived from `hash(tenantId|source|advisoryId|provenance.sourceArtifactSha)` to keep inserts idempotent in replay. +- **Normalization guardrails:** version ranges must be stored as raw-from-source; no inferred merges. + +## Linkset document +```json +{ + "bsonType":"object", + "required":["_id","tenantId","advisoryId","source","observations","createdAt"], + "properties":{ + "_id":{"bsonType":"objectId"}, + "tenantId":{"bsonType":"string"}, + "advisoryId":{"bsonType":"string"}, + "source":{"bsonType":"string"}, + "observations":{"bsonType":"array","items":{"bsonType":"objectId"}}, + "normalized": { + "bsonType":"object", + "properties":{ + "purls":{"bsonType":"array","items":{"bsonType":"string"}}, + "versions":{"bsonType":"array","items":{"bsonType":"string"}}, + "ranges": {"bsonType":"array","items":{"bsonType":"object"}}, + "severities": {"bsonType":"array","items":{"bsonType":"object"}} + } + }, + "createdAt":{"bsonType":"date"}, + "builtByJobId":{"bsonType":"string"}, + "provenance": {"bsonType":"object","properties":{ + "observationHashes":{"bsonType":"array","items":{"bsonType":"string"}}, + "toolVersion" : {"bsonType":"string"}, + "policyHash" : {"bsonType":"string"} + }} + } +} +``` + +### Linkset invariants +- Built from a set of observation IDs; never overwrites observations. +- Carries the hash list of source observations for audit/replay. +- Deterministic sort: observations sorted by `source, advisoryId, fetchedAt` before hashing. + +## Indexes (Mongo) +- Observations: `{ tenantId:1, source:1, advisoryId:1, provenance.fetchedAt:-1 }` (compound for ingest); `{ provenance.sourceArtifactSha:1 }` unique to avoid dup writes. +- Linksets: `{ tenantId:1, advisoryId:1, source:1 }` unique; `{ observations:1 }` sparse for reverse lookups. + +## Collections +- `advisory_observations` — raw per-source docs (immutable). +- `advisory_linksets` — derived normalized aggregates with observation pointers and hashes. + +## Determinism & replay +- Replay rebuild: order observations by fetchedAt, recompute linkset hash list, ensure byte-identical linkset JSON. +- All timestamps UTC ISO-8601; no server-local time. +- String normalization: lowercase `source`, trim/normalize PURLs, stable sort arrays. + +## Sample documents +See `docs/samples/lnm/observation-ghsa.json` and `docs/samples/lnm/linkset-ghsa.json` (added with this draft) for concrete payloads. + +## Approval path +1) Architecture + Concelier Core review this document. +2) If accepted, freeze JSON Schema and roll into `src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo` migrations. +3) Update consumers (policy/CLI/export) to read from linksets only; deprecate Merge endpoints. + +--- +Tracking: CONCELIER-LNM-21-001/002/101; Sprint 110 blockers (Concelier/Excititor waves). diff --git a/docs/modules/excititor/operations/evidence-api.md b/docs/modules/excititor/operations/evidence-api.md new file mode 100644 index 000000000..0a034f002 --- /dev/null +++ b/docs/modules/excititor/operations/evidence-api.md @@ -0,0 +1,66 @@ +# Excititor Advisory-AI evidence APIs (projection + chunks) + +> Covers the read-only evidence surfaces shipped in Sprints 119–120: `/v1/vex/observations/{vulnerabilityId}/{productKey}` and `/v1/vex/evidence/chunks`. + +## Scope and determinism + +- **Aggregation-only**: no consensus, severity merging, or reachability. Responses carry raw statements plus provenance/signature metadata. +- **Stable ordering**: both endpoints sort by `lastSeen` DESC; pagination uses a deterministic `limit`. +- **Limits**: observation projection default `limit=200`, max `500`; chunk stream default `limit=500`, max `2000`. +- **Tenancy**: reads respect `X-Stella-Tenant` when provided; otherwise fall back to `DefaultTenant` configuration. +- **Auth**: bearer token with `vex.read` scope required. + +## `/v1/vex/observations/{vulnerabilityId}/{productKey}` + +- **Response**: JSON object with `vulnerabilityId`, `productKey`, `generatedAt`, `totalCount`, `truncated`, `statements[]`. +- **Statement fields**: `observationId`, `providerId`, `status`, `justification`, `detail`, `firstSeen`, `lastSeen`, `scope{key,name,version,purl,cpe,componentIdentifiers[]}`, `anchors[]`, `document{digest,format,revision,sourceUri}`, `signature{type,keyId,issuer,verifiedAt}`. +- **Filters**: + - `providerId` (multi-valued, comma-separated) + - `status` (values in `VexClaimStatus`) + - `since` (ISO-8601, UTC) + - `limit` (ints within bounds) +- **Mapping back to storage**: + - `observationId` = `{providerId}:{document.digest}` + - `document.digest` locates the raw record in `vex_raw`. + - `anchors` contain JSON pointers/paragraph locators from source metadata. + +Headers: +- `Excititor-Results-Truncated: true|false` +- `Excititor-Results-Total: ` + +## `/v1/vex/evidence/chunks` + +- **Query params**: `vulnerabilityId` (required), `productKey` (required), optional `providerId`, `status`, `since`, `limit`. +- **Response**: **NDJSON** stream; each line is a `VexEvidenceChunkResponse`. +- **Chunk fields**: `observationId`, `linksetId`, `vulnerabilityId`, `productKey`, `providerId`, `status`, `justification`, `detail`, `scopeScore` (from confidence or signals), `firstSeen`, `lastSeen`, `scope{...}`, `document{digest,format,sourceUri,revision}`, `signature{type,subject,issuer,keyId,verifiedAt,transparencyRef}`, `metadata` (flattened additionalMetadata). +- **Headers**: same truncation/total headers as projection API. +- **Streaming guidance (SDK/clients)**: + - Use HTTP client that supports response streaming; read line-by-line and JSON-deserialize per line. + - Treat stream as unbounded list up to `limit`; do not assume array brackets. + - Back-off or paginate by adjusting `since` or narrowing providers/statuses. + +## `/v1/vex/attestations/{attestationId}` + +- **Purpose**: Lookup attestation provenance (supplier ↔ observation/linkset ↔ product/vulnerability) without touching consensus. +- **Response**: `VexAttestationPayload` with fields: + - `attestationId`, `supplierId`, `observationId`, `linksetId`, `vulnerabilityId`, `productKey`, `justificationSummary`, `issuedAt`, `metadata{}`. +- **Semantics**: + - `attestationId` matches the export/attestation ID used when signing (Resolve/Worker flows). + - `observationId`/`linksetId` map back to evidence identifiers; clients can stitch provenance for citations. +- **Auth**: `vex.read` scope; tenant header optional (payloads are tenant-agnostic). + +## Error model + +- Standard API envelope with `ValidationProblem` for missing required params. +- `scope` failures return `403` with problem details. +- Tenancy parse failures return `400`. + +## Backwards compatibility + +- No legacy routes are deprecated by these endpoints; they are additive and remain aggregation-only. + +## References + +- Implementation: `src/Excititor/StellaOps.Excititor.WebService/Program.cs` (`/v1/vex/observations/**`, `/v1/vex/evidence/chunks`). +- Telemetry: `src/Excititor/StellaOps.Excititor.WebService/Telemetry/EvidenceTelemetry.cs` (`excititor.vex.observation.*`, `excititor.vex.chunks.*`). +- Data model: `src/Excititor/StellaOps.Excititor.WebService/Contracts/VexObservationContracts.cs`, `Contracts/VexEvidenceChunkContracts.cs`. diff --git a/docs/modules/scanner/operations/entrytrace-cadence.md b/docs/modules/scanner/operations/entrytrace-cadence.md new file mode 100644 index 000000000..b01e32d98 --- /dev/null +++ b/docs/modules/scanner/operations/entrytrace-cadence.md @@ -0,0 +1,40 @@ +# EntryTrace Heuristic Review Cadence + +EntryTrace heuristics must stay aligned with competitor techniques and new runtime behaviours. This cadence makes updates predictable and deterministic. + +## Objectives +- Refresh shell/launcher heuristics quarterly using the latest gap analysis in `docs/benchmarks/scanner/scanning-gaps-stella-misses-from-competitors.md`. +- Re-run explain-trace fixtures to confirm deterministic outputs and document any newly unsupported constructs. +- Ensure operator-facing explainability stays in sync with emitted diagnostics and metrics. + +## Cadence +- **Frequency:** Quarterly (Jan, Apr, Jul, Oct) or sooner when critical regressions are discovered. +- **Owners:** EntryTrace Guild with QA Guild pairing. +- **Inputs:** Gap benchmark doc, new runtime samples from support channels, and anonymised customer repros (when permitted). +- **Outputs:** + - Updated heuristics/diagnostics in `StellaOps.Scanner.EntryTrace` with deterministic fixtures. + - Changelog entry in `src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace/TASKS.md`. + - Sprint log updates under the active `SPRINT_0138_0000_0001_scanner_ruby_parity.md` when cadence items land. + +## Workflow +1) **Collect & triage signals** + - Parse new gaps from the benchmark doc; map each to an EntryTrace detector area (shell parser, interpreter tracer, PATH resolver). + - Classify as _coverage gap_, _precision issue_, or _observability gap_. +2) **Fixture-first update** + - Add/extend fixtures in `StellaOps.Scanner.EntryTrace.Tests/Fixtures` before modifying code. + - Use deterministic serializers to keep fixture outputs byte-stable. +3) **Implement & validate** + - Update analyzers/diagnostics; run `dotnet test src/Scanner/__Tests/StellaOps.Scanner.EntryTrace.Tests/StellaOps.Scanner.EntryTrace.Tests.csproj --nologo --verbosity minimal`. + - Confirm metrics counters (`entrytrace_*`) and explain-trace text stay consistent. +4) **Record explainability** + - Update explain-trace catalog (diagnostic enum descriptions) when new reasons are introduced. + - Add operator notes to sprint log if remediation guidance changes. +5) **Publish** + - Attach a brief summary to the sprint Execution Log and to `TASKS.md` with date + scope. + +## Fail-safe & rollback +- Keep previous fixture baselines; if a heuristic widens too far, revert to prior fixture sets to restore determinism. +- Prefer additive diagnostics over behavioural regressions; when behaviour must change, document it in the sprint log and `TASKS.md`. + +## Ownership transitions +- If the cadence cannot run on schedule, mark the relevant sprint task `BLOCKED` with the reason and hand off to the Project Manager to re-staff before the next window. diff --git a/docs/samples/lnm/linkset-ghsa.json b/docs/samples/lnm/linkset-ghsa.json new file mode 100644 index 000000000..91a8d6b19 --- /dev/null +++ b/docs/samples/lnm/linkset-ghsa.json @@ -0,0 +1,20 @@ +{ + "_id": "0000000000000000000000aa", + "tenantId": "demo-tenant", + "source": "ghsa", + "advisoryId": "GHSA-xxxx-yyyy", + "observations": [ "000000000000000000000001" ], + "normalized": { + "purls": [ "pkg:npm/example" ], + "versions": [ "1.2.3" ], + "ranges": [ { "type": "semver", "events": [ { "introduced": "0" }, { "fixed": "1.2.4" } ] } ], + "severities": [ { "system": "cvssv3.1", "score": 7.5, "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N" } ] + }, + "createdAt": "2025-10-06T12:05:00Z", + "builtByJobId": "linkset-builder-456", + "provenance": { + "observationHashes": [ "sha256:abc123" ], + "toolVersion": "lnm-1.0.0", + "policyHash": "sha256:def456" + } +} diff --git a/docs/samples/lnm/observation-ghsa.json b/docs/samples/lnm/observation-ghsa.json new file mode 100644 index 000000000..b851fd8dd --- /dev/null +++ b/docs/samples/lnm/observation-ghsa.json @@ -0,0 +1,24 @@ +{ + "_id": "000000000000000000000001", + "tenantId": "demo-tenant", + "source": "ghsa", + "advisoryId": "GHSA-xxxx-yyyy", + "title": "Example GHSA vuln", + "summary": "Example summary", + "severities": [ { "system": "cvssv3.1", "score": 7.5, "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N" } ], + "affected": [ { + "purl": "pkg:npm/example@1.2.3", + "versions": [ "1.2.3" ], + "ranges": [ { "type": "semver", "events": [ { "introduced": "0" }, { "fixed": "1.2.4" } ] } ] + } ], + "references": [ "https://github.com/example/advisory" ], + "weaknesses": [ "CWE-79" ], + "published": "2025-10-01T00:00:00Z", + "modified": "2025-10-05T00:00:00Z", + "provenance": { + "sourceArtifactSha": "sha256:abc123", + "fetchedAt": "2025-10-06T12:00:00Z", + "ingestJobId": "ingest-123" + }, + "ingestedAt": "2025-10-06T12:01:00Z" +} diff --git a/offline/notifier/templates/attestation/tmpl-attest-expiry-warning.slack.en-us.template.json b/offline/notifier/templates/attestation/tmpl-attest-expiry-warning.slack.en-us.template.json new file mode 100644 index 000000000..7b14512fd --- /dev/null +++ b/offline/notifier/templates/attestation/tmpl-attest-expiry-warning.slack.en-us.template.json @@ -0,0 +1,16 @@ +{ + "schemaVersion": "notify.template@1", + "templateId": "tmpl-attest-expiry-warning-slack-en-us", + "tenantId": "bootstrap", + "channelType": "slack", + "key": "tmpl-attest-expiry-warning", + "locale": "en-us", + "renderMode": "markdown", + "format": "slack", + "description": "Slack reminder for attestations approaching their expiration window.", + "body": ":warning: Attestation for `{{payload.subject.digest}}` expires {{expires_in payload.attestation.expiresAt event.ts}}\nRepo: `{{payload.subject.repository}}`{{#if payload.subject.tag}} ({{payload.subject.tag}}){{/if}}\nSigner: `{{fingerprint payload.signer.kid}}` ({{payload.signer.algorithm}})\nIssued: {{payload.attestation.issuedAt}} · Expires: {{payload.attestation.expiresAt}}\nRenewal steps: {{link \"Docs\" payload.links.docs}} · Console: {{link \"Open\" payload.links.console}}\n", + "metadata": { + "author": "notifications-bootstrap", + "version": "2025-11-16" + } +} diff --git a/offline/notifier/templates/deprecation/tmpl-api-deprecation.email.en-us.template.json b/offline/notifier/templates/deprecation/tmpl-api-deprecation.email.en-us.template.json new file mode 100644 index 000000000..d0ba60ade --- /dev/null +++ b/offline/notifier/templates/deprecation/tmpl-api-deprecation.email.en-us.template.json @@ -0,0 +1,16 @@ +{ + "schemaVersion": "notify.template@1", + "templateId": "tmpl-api-deprecation-email-en-us", + "tenantId": "bootstrap", + "channelType": "email", + "key": "tmpl-api-deprecation", + "locale": "en-us", + "renderMode": "html", + "format": "email", + "description": "Email notification for retiring Notifier API versions.", + "body": "

Notifier API deprecation notice

\n

The Notifier API v1 endpoints are scheduled for sunset on {{metadata.sunset}}.

\n
    \n
  • Paths affected: {{metadata.paths}}
  • \n
  • Scope: notify.*
  • \n
  • Replacement: {{metadata.replacement}}
  • \n
\n

Action: {{metadata.action}}

\n

Details: Deprecation bulletin

\n", + "metadata": { + "author": "notifications-bootstrap", + "version": "2025-11-17" + } +} diff --git a/offline/notifier/templates/deprecation/tmpl-api-deprecation.slack.en-us.template.json b/offline/notifier/templates/deprecation/tmpl-api-deprecation.slack.en-us.template.json new file mode 100644 index 000000000..c0d4a4ebc --- /dev/null +++ b/offline/notifier/templates/deprecation/tmpl-api-deprecation.slack.en-us.template.json @@ -0,0 +1,16 @@ +{ + "schemaVersion": "notify.template@1", + "templateId": "tmpl-api-deprecation-slack-en-us", + "tenantId": "bootstrap", + "channelType": "slack", + "key": "tmpl-api-deprecation", + "locale": "en-us", + "renderMode": "markdown", + "format": "slack", + "description": "Slack notice for retiring Notifier API versions.", + "body": ":warning: Notifier API v1 is being deprecated.\nSunset: {{metadata.sunset}}\nPaths affected: {{metadata.paths}}\nDocs: {{link \"Deprecation details\" metadata.docs}}\nAction: {{metadata.action}}\n", + "metadata": { + "author": "notifications-bootstrap", + "version": "2025-11-17" + } +} diff --git a/offline/telemetry/dashboards/ledger/alerts.yml b/offline/telemetry/dashboards/ledger/alerts.yml new file mode 100644 index 000000000..1f2922e7c --- /dev/null +++ b/offline/telemetry/dashboards/ledger/alerts.yml @@ -0,0 +1,39 @@ +groups: + - name: ledger-observability + interval: 30s + rules: + - alert: LedgerWriteLatencyHighP95 + expr: histogram_quantile(0.95, sum(rate(ledger_write_latency_seconds_bucket[5m])) by (le, tenant)) > 0.12 + for: 10m + labels: + severity: warning + annotations: + summary: "Ledger write latency p95 high (tenant {{ $labels.tenant }})" + description: "ledger_write_latency_seconds p95 > 120ms for >10m. Check DB/queue." + + - alert: ProjectionLagHigh + expr: max_over_time(ledger_projection_lag_seconds[10m]) > 30 + for: 10m + labels: + severity: critical + annotations: + summary: "Ledger projection lag high" + description: "projection lag over 30s; projections falling behind ingest." + + - alert: MerkleAnchorFailures + expr: sum(rate(ledger_merkle_anchor_failures_total[15m])) by (tenant, reason) > 0 + for: 15m + labels: + severity: critical + annotations: + summary: "Merkle anchor failures (tenant {{ $labels.tenant }})" + description: "Anchoring failures detected (reason={{ $labels.reason }}). Investigate signing/storage." + + - alert: AttachmentFailures + expr: sum(rate(ledger_attachments_encryption_failures_total[10m])) by (tenant, stage) > 0 + for: 10m + labels: + severity: warning + annotations: + summary: "Attachment pipeline failures (tenant {{ $labels.tenant }}, stage {{ $labels.stage }})" + description: "Attachment encryption/sign/upload reported failures in the last 10m." diff --git a/offline/telemetry/dashboards/ledger/ledger-observability.json b/offline/telemetry/dashboards/ledger/ledger-observability.json new file mode 100644 index 000000000..b1e675a61 --- /dev/null +++ b/offline/telemetry/dashboards/ledger/ledger-observability.json @@ -0,0 +1,91 @@ +{ + "id": null, + "title": "StellaOps Findings Ledger", + "timezone": "utc", + "schemaVersion": 39, + "version": 1, + "refresh": "30s", + "tags": ["ledger", "findings", "stellaops"], + "panels": [ + { + "type": "timeseries", + "title": "Ledger Write Latency (P50/P95)", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }, + "targets": [ + { "expr": "histogram_quantile(0.5, sum(rate(ledger_write_latency_seconds_bucket{tenant=\"$tenant\"}[5m])) by (le))", "legendFormat": "p50" }, + { "expr": "histogram_quantile(0.95, sum(rate(ledger_write_latency_seconds_bucket{tenant=\"$tenant\"}[5m])) by (le))", "legendFormat": "p95" } + ], + "fieldConfig": { "defaults": { "unit": "s" } } + }, + { + "type": "timeseries", + "title": "Write Throughput", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }, + "targets": [ + { "expr": "sum(rate(ledger_events_total{tenant=\"$tenant\"}[5m])) by (event_type)", "legendFormat": "{{event_type}}" } + ], + "fieldConfig": { "defaults": { "unit": "ops" } } + }, + { + "type": "timeseries", + "title": "Projection Lag", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }, + "targets": [ + { "expr": "max(ledger_projection_lag_seconds{tenant=\"$tenant\"})", "legendFormat": "lag" } + ], + "fieldConfig": { "defaults": { "unit": "s" } } + }, + { + "type": "timeseries", + "title": "Merkle Anchor Duration", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 }, + "targets": [ + { "expr": "histogram_quantile(0.95, sum(rate(ledger_merkle_anchor_duration_seconds_bucket{tenant=\"$tenant\"}[5m])) by (le))", "legendFormat": "p95" } + ], + "fieldConfig": { "defaults": { "unit": "s" } } + }, + { + "type": "stat", + "title": "Merkle Anchor Failures (5m)", + "gridPos": { "h": 4, "w": 6, "x": 0, "y": 16 }, + "targets": [ + { "expr": "sum(rate(ledger_merkle_anchor_failures_total{tenant=\"$tenant\"}[5m]))", "legendFormat": "fail/s" } + ], + "options": { "reduceOptions": { "calcs": ["lastNotNull"] } } + }, + { + "type": "stat", + "title": "Attachment Failures (5m)", + "gridPos": { "h": 4, "w": 6, "x": 6, "y": 16 }, + "targets": [ + { "expr": "sum(rate(ledger_attachments_encryption_failures_total{tenant=\"$tenant\"}[5m])) by (stage)", "legendFormat": "{{stage}}" } + ], + "options": { "reduceOptions": { "calcs": ["lastNotNull"] } } + }, + { + "type": "stat", + "title": "Ledger Backlog", + "gridPos": { "h": 4, "w": 6, "x": 12, "y": 16 }, + "targets": [ + { "expr": "sum(ledger_ingest_backlog_events{tenant=\"$tenant\"})", "legendFormat": "events" } + ] + } + ], + "templating": { + "list": [ + { + "name": "tenant", + "type": "query", + "label": "Tenant", + "datasource": null, + "query": "label_values(ledger_events_total, tenant)", + "refresh": 1, + "multi": false, + "includeAll": false + } + ] + }, + "annotations": { "list": [] }, + "time": { "from": "now-6h", "to": "now" }, + "timepicker": { "refresh_intervals": ["30s", "1m", "5m", "15m", "1h"] } +} diff --git a/ops/mongo/taskrunner/20251106-task-runner-baseline.mongosh b/ops/mongo/taskrunner/20251106-task-runner-baseline.mongosh new file mode 100644 index 000000000..422f629d8 --- /dev/null +++ b/ops/mongo/taskrunner/20251106-task-runner-baseline.mongosh @@ -0,0 +1,125 @@ +// Task Runner baseline collections and indexes +// Mirrors docs/modules/taskrunner/migrations/pack-run-collections.md (last updated 2025-11-06) + +function ensureCollection(name, validator) { + const existing = db.getCollectionNames(); + if (!existing.includes(name)) { + db.createCollection(name, { validator, validationLevel: "moderate" }); + } else if (validator) { + db.runCommand({ collMod: name, validator, validationLevel: "moderate" }); + } +} + +const runValidator = { + $jsonSchema: { + bsonType: "object", + required: ["planHash", "plan", "failurePolicy", "requestedAt", "createdAt", "updatedAt", "steps"], + properties: { + _id: { bsonType: "string" }, + planHash: { bsonType: "string" }, + plan: { bsonType: "object" }, + failurePolicy: { bsonType: "object" }, + requestedAt: { bsonType: "date" }, + createdAt: { bsonType: "date" }, + updatedAt: { bsonType: "date" }, + steps: { + bsonType: "array", + items: { + bsonType: "object", + required: ["stepId", "status", "attempts"], + properties: { + stepId: { bsonType: "string" }, + status: { bsonType: "string" }, + attempts: { bsonType: "int" }, + kind: { bsonType: "string" }, + enabled: { bsonType: "bool" }, + continueOnError: { bsonType: "bool" }, + maxParallel: { bsonType: ["int", "null"] }, + approvalId: { bsonType: ["string", "null"] }, + gateMessage: { bsonType: ["string", "null"] }, + lastTransitionAt: { bsonType: ["date", "null"] }, + nextAttemptAt: { bsonType: ["date", "null"] }, + statusReason: { bsonType: ["string", "null"] } + } + } + }, + tenantId: { bsonType: ["string", "null"] } + } + } +}; + +const logValidator = { + $jsonSchema: { + bsonType: "object", + required: ["runId", "sequence", "timestamp", "level", "eventType", "message"], + properties: { + runId: { bsonType: "string" }, + sequence: { bsonType: "long" }, + timestamp: { bsonType: "date" }, + level: { bsonType: "string" }, + eventType: { bsonType: "string" }, + message: { bsonType: "string" }, + stepId: { bsonType: ["string", "null"] }, + metadata: { bsonType: ["object", "null"] } + } + } +}; + +const artifactsValidator = { + $jsonSchema: { + bsonType: "object", + required: ["runId", "name", "type", "status", "capturedAt"], + properties: { + runId: { bsonType: "string" }, + name: { bsonType: "string" }, + type: { bsonType: "string" }, + status: { bsonType: "string" }, + capturedAt: { bsonType: "date" }, + sourcePath: { bsonType: ["string", "null"] }, + storedPath: { bsonType: ["string", "null"] }, + notes: { bsonType: ["string", "null"] }, + expression: { bsonType: ["object", "null"] } + } + } +}; + +const approvalsValidator = { + $jsonSchema: { + bsonType: "object", + required: ["runId", "approvalId", "requestedAt", "status"], + properties: { + runId: { bsonType: "string" }, + approvalId: { bsonType: "string" }, + requiredGrants: { bsonType: "array", items: { bsonType: "string" } }, + stepIds: { bsonType: "array", items: { bsonType: "string" } }, + messages: { bsonType: "array", items: { bsonType: "string" } }, + reasonTemplate: { bsonType: ["string", "null"] }, + requestedAt: { bsonType: "date" }, + status: { bsonType: "string" }, + actorId: { bsonType: ["string", "null"] }, + completedAt: { bsonType: ["date", "null"] }, + summary: { bsonType: ["string", "null"] } + } + } +}; + +ensureCollection("pack_runs", runValidator); +ensureCollection("pack_run_logs", logValidator); +ensureCollection("pack_artifacts", artifactsValidator); +ensureCollection("pack_run_approvals", approvalsValidator); + +// Indexes for pack_runs +db.pack_runs.createIndex({ updatedAt: -1 }, { name: "pack_runs_updatedAt_desc" }); +db.pack_runs.createIndex({ tenantId: 1, updatedAt: -1 }, { name: "pack_runs_tenant_updatedAt_desc", sparse: true }); + +// Indexes for pack_run_logs +db.pack_run_logs.createIndex({ runId: 1, sequence: 1 }, { unique: true, name: "pack_run_logs_run_sequence" }); +db.pack_run_logs.createIndex({ runId: 1, timestamp: 1 }, { name: "pack_run_logs_run_timestamp" }); + +// Indexes for pack_artifacts +db.pack_artifacts.createIndex({ runId: 1, name: 1 }, { unique: true, name: "pack_artifacts_run_name" }); +db.pack_artifacts.createIndex({ runId: 1 }, { name: "pack_artifacts_run" }); + +// Indexes for pack_run_approvals +db.pack_run_approvals.createIndex({ runId: 1, approvalId: 1 }, { unique: true, name: "pack_run_approvals_run_approval" }); +db.pack_run_approvals.createIndex({ runId: 1, status: 1 }, { name: "pack_run_approvals_run_status" }); diff --git a/out/coverage/ledger/4d714ddd-216e-4643-ba81-2b8a4ffda218/coverage.cobertura.xml b/out/coverage/ledger/4d714ddd-216e-4643-ba81-2b8a4ffda218/coverage.cobertura.xml new file mode 100644 index 000000000..6223ecde5 --- /dev/null +++ b/out/coverage/ledger/4d714ddd-216e-4643-ba81-2b8a4ffda218/coverage.cobertura.xml @@ -0,0 +1,71696 @@ + + + + /mnt/e/dev/git.stella-ops.org/src/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/out/coverage/ledger/e4bdc625-4088-44fb-ad98-bb084fa8e84b/coverage.cobertura.xml b/out/coverage/ledger/e4bdc625-4088-44fb-ad98-bb084fa8e84b/coverage.cobertura.xml new file mode 100644 index 000000000..64c9e5fe4 --- /dev/null +++ b/out/coverage/ledger/e4bdc625-4088-44fb-ad98-bb084fa8e84b/coverage.cobertura.xml @@ -0,0 +1,71698 @@ + + + + /mnt/e/dev/git.stella-ops.org/src/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/out/tools/pack.binlog b/out/tools/pack.binlog new file mode 100644 index 0000000000000000000000000000000000000000..917103088ca59a83ebc240e04b02f26227d76a21 GIT binary patch literal 453067 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

|u&DfcYEzb@7bGl<0{>{XF^HJ>} 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 z zbEi_ot9_iV|j*?Mg zd-fLC-N-$vDh#NT`fV>S=1RtWV@r14Y3f|%nJu#2mJ#Q=tX)xMIa_ta#GO5XA{^ws zCmW-jg$yrk8?#2O6wv77R>CXxNj~)PD|^L@W!iPN7MGs; zuk+(W#z)JvMBymrW6MLZm14HeS{Ol~WkrgQtD-m@w|N!s?!W6oB`#`ahPm4D@*629 zZJ7Dw-!=@kphHnEByiK=^!`hwc)KJ|?&Ft=EwZvMkIk8$ZnG|ml zBuLL{<^)iL@0GKuE76uy9~PEE2SiYODYJy5_9wsR#eTW}?*y1%+eV;rn&do+uh5$K z$nog64 zHx$PtB;ekeCGpYOPD)#EN<`dypExSawJOiQK)ZaaHTUSpXk@O3L<`bU`Db%KwzlbN zo?CP~L4!uQM;9*YDJRN!!pmu&L-S?shr+*~O(UWk$0}0AX=jUisAFJUF0Y@lN3Ubc z?NX1d&)~i}uvOg$C%Ac5>a?*5sE+EE)7WRrXn%aIC=vhIa3t7t>YtL9sOgD0#l2dl zlXIG_)jC?2<30z$QS%CD$UV`FymT5a#w-pa!?!%sWSLl#xtS` zt|EjiZ?E62^;jj;Cz0jHnbMaIEv&`tU%PnbtsNKirAP9r^rb{Cx}+kOJMT^;dH)J( zHyFCcemiukj)~$4yjRF^172d$3qV!sCqMP}{IGRa(n4|bX+zpoBvNPR2H0w4Ft|+A zf}pmsNl8==J+9}>{|e|8x%Dfx7JZ8bIiIhJ|11u|VUKFHog z#T{SyBFZ1}YDOqw=qEPf(7d2fSb~prkB3|Dt}W0y4`DC4;%Y2svy!H&hb69;s~3IwfzS^`?EEhD2tYq}7W zyii!R&-Z>Hu~i^3XLA^nIL5Zob#(~r;H2?`TJzk{)^h`6+#*rs7udmg-3K+nk)cFA z{eGYI9yY-8v;K|+iUS99^%M*_0}I5-TU&9u)#nQkpPxv9A-P~Ipd9Kzi@X4_^La2B zV!SB>XjubVHMUPeN>p)+6qOOM1I$9eaQx6#&bBE;5=;pwy#bUk_`8CTO(9*I^nea8 zptEWd5I{>>L#>OmJ?$$Qn|<;aHz@YP7Pm+pfC-dO7yxiLZqQ&BZ;0ZJWGL)F{09KL z<-oKtgJ0i}MV58w)whR9sZbJULY&zRoAsug9mq#~MIXy>4XGcu*ID z90-Q|1!M5azq5SD;Z~2v69nOZGeiN`Oy`cM?bHW=jBXAq!Hj?*UWfp2;R7-sK|yL} z3~@r}4&eY4!9ax1gTXa%p)k3y(~(>BwBVYNQK~Loh`G5bWEzYDp!1vEt)7otvI_=+ zBGwYzOBT2-5s55YlEk*S!N&;^pZrHTHz4=%!7bNRFa!n`0+hZ2O6*+3&OH{S&VqOF z0Kb|6B`9&MkUd+R5Z+fH3G=Fv(ds5$$k41Qq#TR`#3uzPA#oC)&v1}BasaI|Gnkdr z7noJpIapkjSvxs(U8Bw?cnNcR0{#&wgtZpXqFU!;E*TEn3qb&Ka4#PjJuB9Qm^9Pa zvu)ATxTkzW2T5Vg9!qF?}f?$N}gpSqc{1wkbE&PVY~_fcZF9mg18LnV1BO6#K1NeLKGY#vK!gp&Kw9a#kdCQ-ALU+Kphwhb?pRDI zaX^=1K~UZcdfuS?e+7gh%Zd20w|^{2okfJhuei0TQt6ll{5}vhw?cC2GfkZjy>lq+b9i173^A>{5TIP5F65lg6e6%G4&brI;#TiV zPkesg0fsbfTJHoZ?+yb^%=E-HzN)0ol&x=o;h7zPsP^mNBW{n+aqows*CJk3AR=MB z*p_o+(sOecG=K^3fEE; z@{cC&maG-%+;*WQJ)5dyvH3c0|$vKwJz4`h847pnz>!z;;Q9E~F3`FeQEhJ#7D! z*{!~tn%McQ8w}x=HuOf&7AwjPw+owp+fSFl|D zM0!pE`fm~{0F>gfH;jf&AqZg9nU^xij;z{EYX8=5H`aC_MkHW{%LV9!y?>=%+xvYU zdqIGiMb#Y(XCV&bS11T74SEZhu?Ni5Va2vUq~g3$<3db=J#L)nzSsKn_aPzoyF`V) ziITToWOA#wq#!;oF$F=3@8$wB;p9Ocg6KnH$Z|kO z^|A-5Rc#8(eT%XE{ALKK`lKy@ap_<6B>oD7^92-hpzPHA$*G-xE(&|Q`+Z7#g@Fjy zRCX-F`8c5UpFz+>@PF*p04yt#5uc|ugP>#}IKVqO;N2NhYW0CG&YM^TnA{xtW!0Lo z`@QpCLt&Glia@O}GDb$#z`BscOH)WJm=~zkE~8t09!Ze*2s;R>4n_wIj{+^6B*Zm# zq(fpjB>-N{`asC`rm(kzAm|jB91w{CM0kjaou?{5P$UpQCwBx#yD1zK$>$5ZuJHP#Li zwz14V1E$r)0gA!EFny{>m>e%Vz%XY@V1V?z=7{<@K;4cvl>|yy=>RaSp?53IFciiFvIh(| zkOSj`eXUPb#ax8Y6yGsWRc4@R-R7cUSE$z~H3ML;s5cof7g$|Hh4O+StmA$Ft~_!( z7K@}D&?5t2njRkpsEQXiGHL|^)^leeuoAvJAd53DfItv1O!AR3PAGL57@f(Q|I0LN za8(auC~NS)wU2KdfW~mx;PHR>gfF84BX|WMQyVtuzXwNJ6Ss`GJ>6Cc$ovc&6#FMn z>P*#e|9LlztX?{1c8o~h+0@~{i}o+$q7H< zpMK_s{Cjih>E?K+OH1uU5&flzq9n7m=h!ig)&4?{)C|aDw9{1N8iL zJHA}mO1@~%nGF1QY(@Ksv1sF`N1jyGOcnO5HW%H~oQQVk)A<>7+Q4vqMNoyIEXxM2 zjRL>>B)!sLa_UIhSe$;ntSWu+QpLh-e2J_Omlpo1@=k+xRq3AgIVYc06OM^`n}8Bs z8%F5yp?s^~L&taEAGZMQ_GXZ#_FGKYME@Y8Yw?34ud8mp)oad=a*_GkD!Gyk#l^#4 zsGRNunNnn^L-fGHlwm-l$4zfhLl^SQ^A0*x5GZC3)gGH)adsHM8g49g~JbK zpNO=a^y_}%7pbveT_n9s>O{*&(u71f2 zm+l9-e=PgTd6y8t0+$Y|bPs(@bW`vW;I2S=c=yXt2^~EwXBT3)`C@J(2`3-OnCB26 z$vHn#j2ZNfZ)snf??U=KJ`7$`it1#9Iq_IHbG^yV&g*nDCJb*%VJ0O&6PHj8S;$3Wy)?%rv zTxCnhFuC_vpd(hs#n_C_k&E`Mtj0IkZ0xDj1KGuYa||NC2U11-GHJx~MdrH5b4F%drUZVA7!Cta#CFV%yl*6CV?K zi#@fj9{KF6$y`M!&9#x7h`e3AB<^Qz>nol+vYq`c>ZzTlG&`^+Wrp?oG;03iGO<Fn5wm%8qUK%opv0!!A zV-$@oHHg7JN{W3R-$%g+3wY=EB>vzNzqhh|ciW(k0Nh`<=C_(;_xt-Vk<{{6Oi?vtd98Y}IFDOA)R!vn$eC`)hDgk6cR$fh0&Yr@x(asnB(KzeP z4D^aypL|`m`bP=4QG22j8PwMxZ`1<#eYbp1{|*eNFb7=_3Bz!r2Xe&>5c?T(sJZ^_ zxBL#&buCmSJVwIKn%+8dks~o$O(8lSfx2ul3ORbQwFhcZKB}|?UGQZ&Lvt~~GjD`p zupLX6DH>r|w5-{W3DqTQ-l(3b>|HMg^KA-bX31ej^!siiF~gdB6il+=EUZtw_P{O& z$8I(WfzkS@%LZP*ds&j2~=rzaRGhEGQkvb8yAhl6notVI7ck{roHdi)kHaO-GE>o+TIj zeC-yXrHp@^rH`#%vO+JEc=@3&)As`S!o{(=(cfRn5_NM8N)p9$a7xdab4H4tNpo!q zfsu1-#h*Evl*;QA9Tn^dG(UA0klT>btT|?~oWJ`4dCKMg9;hfL5dUopb^pTRH_1PC z0;GTT?iGhWu(?^LPSh>@KY8}@l;WF!_8@ugK2#d>X{t(%XqoBBF0}~Mj^v5*;l7Nn zC)(2e!2&#LM1~EZPa$JgE8R^Fl5Ma)2^YcbRJDJ2EMtRLZRKxrpA*C8cf0D341O7I zXXn0r8ZR7VXk)|D8S4aI@rE)KdYzs~Jt}V{zi)E?UH$w$E=Be`&1xgmgnR00qf0PA zT7kUqL*I=0tESkO;riWBsCi=fp+&Gx9}EEcNI8f1*lQ zNrA!!${QS{?AZ6p#Z$2-Ov};M^#rz559)Hw|AM((f)^i?H2rPUH0|7ScwdF>tBgyV zwGZ9$cz>;0Db1O6rX1*-As@5tL$-TmRYAPzY1jji)IJJ_ugB)VD2h9bG;mH60}QhMKtU;fVxB)(lyWxIXg zo97-NAV2l^HrFothqDP~F*(@`&T~ULv~A<~#52#l+q+xmYz1pH_dZsi+k!6Fe}XcW zzx@x>ie=^NFw`?jAT8h&4W1bP!y-N-0X=l-Ff7E#frU+TAc3~+9X|sP8q1NsM(xiK z^cW-}LZZ5#X3~q$x?8B&=acpGA6fpX{)#T^M!^I7{{M#Q#vtyKKlu2pf9L#IL`G+( zAinh%15Ov=X@%8@8|KH*mB`SM$$Y~iRO2CRtUzE@SdIG;O(Axa1ydXo#&qcn+|`WN zW=KuwLqOYmOC4cYs5xR|CsslDF|Lt{u8jp=$JHS6<#BvvAt3UeXZ(Ak6I;4tN1q$H zRWE0eegmvrhNOIGTAh~I_Tr6UxkjO)(#dOC+IJ1*>fJ-fPLJB^9A-1*2q*sUzwVf% zOytf+OpJ5$IOOghWSzzPm5SSYl0E9*G*y)lTni#>zf5c87pEN2eky#B!3txDD%geO z6`rRml2#Ba^s#)FC;#o%N#$9{M#tLmk;vuIo+2$zTM{!cs`xE^((ifl#yH#&B68_g zI(;rYgz>5(wS=x6Buf%*Wmc(2nh`%S z(Apfj#S%CKPK#UR(k%S=t1~B#l?~P%iQ1Rq*30%IA1-R@2uop5*OoLRyx7d}B{6nr z7bGg)@>WxW^-W@O!O|}KUqL&(y%RboS{QCLW3`i$*>@MkR)w{`4;(4N{t?QC65@R> ziM1|uBrTdKX$o8kva=*kL_}28UNHk7@SC*0DX`Uin&+j=ikcWE24mD=au6x^w|@Q# z(QwpF5^cFgTGosbAQ3b0#Y1jw)|a_1A=@*&eV6CXGo&99dcR~A_q$wZ3bfxx7dXjC z%8r15QOB6!g_!?%M}}r@>#sgqgY}S!9rJ0cioVE(Pka^xT%mxOKOAUf7O(RSzsQUY zxfP-jh;qcHiK4WN$NRRbuNa?AIM4Fy!F?uSa)jk-HEo!ChNR{}gvbL+K>?xL%B>|G zRhvk=NXy-$`nVI9>0hVZGb+01EP3~}i5#&#DHKtJ*|M3Vd~sFd^f*oQZ|6VA*Af~;mQMw@Y6KV8>qr`RkQ(FeSedU?M245;hf(uKy-t6% zJBXo9D3=R-ANOmF2caeVRJ}c7b<2b}^XzUt58eQ7IdV=>RFZo z4gKo9!8ys(vi!?`$^ zSRbj|vhh#mjkn{+*oO`I*kC&>GR}^cgHE3f8L^8oEKz@gByeDUIa5Qxn3BIQ)HG_cAK)Lk`xO zh)-10H;Z7yifZ&GC;j*R&0U2x3D=~@D!Z1I*EafF2KA2^%6lsQl~Zk@2QL($o+jFT zz5#|Z*HQ5%lcP(A0S?{@IDVX!uW(#ZA6aA^ea3>?v+oDQLDUFq`l5LX8TVMf+YSP{ z@h?YIZK(9QdY1BoNyaxA)WaI zi|qkzoSrP9Z%89rI;z53Se>P#?9Dj{B+j`k;b!s-c!wUMgV=_0tw#b}62G+(jWw@- zHRG_gL0dw|$)6beJ}ZVEG9y!;chG`08dQ*n78X=ck2d-`Le9&;S5X?2^YV~n7~&*hInLA+{AOMuD%e^lCgp;737@mP-uT#q2Q&w_F)>*U7Wmn)83;XG0FV% zT`)9KxS`u^s$G%xNOL45+qU^>5cJ@un^?!CjeP9cfyckJ_wyw(^n08t|5B3lbNsNr zO!TSFtq^9l1!i$CydNeAA9(RU_(Wh+{$p}v_P#ouo_-B!Je3eU%9Ah~@82JR$WI_P z-f8JTR*a;VD$-5C#ZPvRAE&>b>y>MGC)))v2^)jIP6DUsq0yGBFCQ=xRF=NOiTY=y zp(e=0H%oNtm65&tq7litW*1|a9qK+#RN==HAbH|0+zW0>h$|TPyE3Y%kbmQ4+EI7; z$gP|Bx#dEU(p`*mo7B3GD(-PB>SN;3}odw8r?JA3V+s~+V{VI$9`cYHe73wO>_`Rh~=BRRxLoP%Z_S{;FAJS)n z^xIOd_3Ktz78v9fbG&=-HVZi}saV0*RMlMI4zI#I-SKWfQH zkkbWm(#RdZidFOpSibBf*a!F>c`m(KLsaA{-)}|HbXJ0 z%qzd?qiQ%|6Hxc4{VnpvHSY!VoA)|{V}v*GvVmuzM^W~xL>$?jtuMuly5V{7ITCN5 zvBgATn8JH8O$Uq$dRZN@ugP_>$n8)q6ZvDFg{I%jHtM065&UZlJ<`RNqF~5cQ$T$3VCKlhyOUnSDsS}ID8e39bp91o;a+A?pBl;7BMj-(*8IecL zRMTaB;Mqk>dX=afD^yWyI-3sI*o%((G36batfuc!j%kQQR5<0iL2;U zoYFdFGFMq>V`WzH1yqf%2o;=zE$&y#i{=fYXS(Lwv)nt{2gBJ{o0jLvo!a{sprd^m zl*W2yF4*#)DOE1usA|li2_H@&og9dRw=M&sE!rUw-7DBS+a(8nC5OXw%V!2^5E zWujSej$D|8PT67A??lmD_T9q1ZFaXiT9VGhvc?2hPaA+1sXJougARc&D7KZ0TD3ub zu|P|4l3@3-rn_Y_Wv7L|4#tRKs47QW5a{@n^fls5ZGn-=vbAh_bN~6wH1SzriWK=< zchz$BO=%e;!qWQE4F&RT$@m%@E-vib?IgFHefjD`F?x9H!QoWV1VYD=%M=}OYIr}= z1kzk{*Gy*%2IN9#3k&p2ck>Gfmd+N^O9P6D^OsaZmh%<$6n?CS11IN1Wh-e%Yk2U; z;P~d?d}ZLkc>nzBBo?d%9yp}St^F+&sz*?>>F(Jn5kk9r!wp_>0#r*29dq6F=K@Pw}5W765_o#|QkJ1Wb`OzN)X^)RPbY-GW-J;PO8 zy{ylX$lOyrTHPu*Lfr}sDiEu?DEJUh&&~PY`hK!7rNH_a75r(! zR7sz^pD&uyFBb?3+Z4r8!Cs31@v>eJbP&67dEIY`$y(HaU46L*bkq=JduZxqfN;(- zd`Nxvo@gxR9~VIpmTY~=^yM|ZF8G8+#mzJM7`U7_2&RJ+5UZQ3Xdzo?BLYyOkIn}Ttp z$7hjyz`RhLE+NH%b|4mBVBI67h{_sGAdxm!XKN2+$X$$4RwE6rZ@h6GeblR=lls}w z+bvwbqE8V!We6yglQt8~_@>7idqoQ#uCnHdj)DL|8uW~C0+;Zhrs^ii?&_|N4c2X( zE!vfWHQJ4W&El0UgY%1gO1oWC8s3KK7~BT>7|n*NIHfu2J|lV9tyg2(ZFIfIZE$_W zZF2nBbI)q8#oVJ>$b^(xTl-d&KArBe`7Q>xSbW;mc@jA zqd)b}vwcyYSB9A_4+%W>j$|+x8^aEsGy>Pb?O2>d26paI1DHnd4$ia0;3|4Fb z!JfS!aTK5n{*)QNPxxRVgcd!Pl|bJ}&^ZDS0#8PZeLUhw)!Xg~)-|V&Wa)4S)vxb%(Qn(XZpXpbxnNnXSP!M-o)=BNc?J z1j}3FeLxL~`!g$udj=|4{Erj{?($dOAKvNve3JWFhNlxVw}%Z5cRwty@Te?uk?G)k zlFnI%^^;SJ2TUe+d~~k$h%9;GX^ec5;8}*A6Ep1xZEE*^lunB9Eq9^o>O2;?S&qV! zOWB7k5_bc{PLI&7Ji%*$JQj>uj<6Fefrl+zcObaV@!+ko`7R?BkC@n{jX%qcM}&Dy zDiRHUqAhzEIbuw~iq?HoopqD2Qmnw_FL~3jQVhV3)_ff~Xs2N&nt>oMdiXhF^oqNQ zhLLEZW`=3WNKe{;PJRvuvB ziG1o542_OLQ;AfYW*Xi=Y$Ce_`mPNo5$T~4pXfE$09DN4_akGsj03^C?ZCrw9{DzU z#tegbT=M=1yS^rzvJ*hlZaW~b9S6QDoHuD;+_ya**e^XmIBq)(u%3s$Hk`D-{a69S z{BW>*e}LohQvUQR0*d%z0KJX?W8N5GK5Co--WaG}N`SFI3x&9Hk#Fq~9PMr7td`Um+Z&KQ4yfAzZ~jT_WIeTvbRuYT$A_RiIvWKooBcwjU>e znKuTu*BH>mTZ8QP2hhZKg9^-ny8_-z4S3?MLG}BD!~C}c2ONyY3fe0Nkmj{T@C%2_ z@ZCWIF2-Yp>}3N?3E2MjlYr9<+JOL8!D|8SbpTWe*aG=+z-QhLA;UvaZw ziEsJ-;7A_%lX=o6!WP~3T5^jX_@8>lOn@=G;D*A=zvvR%gFU+C z1I0PG>DA{RIq;|CRviMPc+n-op*;4t(Jcf>QCng*UI4H6hx3gb!oP(4VFkL4+hv=!3PDmd$hLi?6Okd0c@zP zclQp-?H-{Bj+GmpIO!CYv(&2TmM|Ky-K45SOlxu_3{1~s@y)fo(-ZmdU?@r8AzudE zfgf45Xy$W^s>8^vxcCK_H1b6%n*@YUmkAhEryJ=&&*261iur|SP~m7AAwvpu+W)=W zVjhwt!`sjm9a>zjMSRD%M&uMOr9F^Xp~)HQF*Z@BGA1F^gvd}5goMU8hm8dcO`%Jq z=W!4JYBnx;3jD{7<@v{qXnqRhn)dmQVRYh}_5r}_7umaS+G~!<oahlx+TbL z_u6y+1;beOtk7db-XZC$> zBOK5_K{&icfZ~J3K-;9Ih_w=sPR^{ZUrZ^3bjpKRapJfh<4;`^WA8gnOseM{j(Mm3 z4?#UPty^O7(R~<3%RVlI@wqmw_4ib#jpyG#{)FlU=5ep1#HnDy6Ud*<&lr7g(1x*I z-jf0;${#O2AlOExJQq_lnsH-fsbZh-DAcddW>ae^q~esk+4Jveu{7Zm2_$t}n!iC-L-q24uJgjp*!V=3>a*A1 zvdfOSzCf%#F)%yq-}85I6#gO=gKg{Zi(+~%$2VDD0$@gG2Ft@&b2-geFO@LY(- zQ)4=uR3x#!T0G$qUadspv3_!Bxsj4UpTenbv4uE0otp z8Ie#4v{cGYDT8k!VZ%s;bK#xlh4~>8x%XTSRcC2X36-@KiN5iaquu=yyBm($fg7Dw z=`Y$e-*xTeUBqxsx}wJ4zs`$-B45l!++9+NSk%_{a2R?N*kgAD$urD6{vsaEsnOUc z8s)*+zBXVNb&ixoyZ<wU!BphMOd>QP<|NJE0$Vlzr36UhGxB;h#r)TQ0u_r?o1sa5wE>gh_ z!+3)z*qxG*r0)D<){-eUTBwW*?fj@@Un=5hM!Q~2R;UaCt$Zs4Q7?~<@#f)vo*E%O zo7K-0V(E=<5PL}`pg0LrP~wQ!X8ptvTUoFY7i*O@!#lnl#gK|TEqS%^MA2 z7<;#1#LX>GnSqCn$w?%4_ z?X5O8X?yPTI`soK+uI#P+pH~Z&(16;8!v#=^A{CW)*}nmz#eRznN6B+l z_2Lb#Ws~%FjUJQwO=`yWnmF{S74aq&g>5bDw#sBq{6LGC?Bl962uTA*QdvsonKQce z$&=)b(xx?1I>w3WB#f#D>8piH;LuM;fqMG&Y#cD91~8*x&15-UvK)hALcgh9hAG+}wiGB6y&s`u) z9%}B@aP7LJbK*BEF}IXOdA$8SeCUApNh&WX@qVJ`RE;+f5%ejSoeg;oIA~=@YwJ?1 z5YX}CqU1GS(;5KBkL4(e7;7n8&=`_>aQfiLYhh?PBDtqT>>!IdsV$y%?Dc#M@i?-h zhJ{G=$|u&#!Ntk)WQ?-biteZ9lIYa*)SVCFxw{wga}t$E=oe3q5Aqa&pcE77D&~bV zKQ&OM43r_@7gM6zZmqF@;wvRZm1!zaCn?c{j`X!?s5A{bZ5Tro``hH=K_y1~K)3JM zlO{`~46h3Csg{=0>o}j^jzuimh7TZf>JX-(ih$=7=!aVs=@|65=MZ@1D5`kC8{(aLOk<|dbKf-4+Q}~sf=`HPTYAlp z?kW&!Z2UFyA=0;r|D4=w|F!Z|Z<-0e-}g5LBH_w_7nuLXE?gL3p3iDDd!dN1CXFGr zWwJ{G#UWF&z_dE6Of{?(A8DY~W@RTASWa%_N=c%Oz(Z798=J$XHerUNb%(cl_MqQ| zwtDuO-XH5OZ{zhUgN;uR;1+`7Z@zqGb zj^y))G>$%M*uceWMw>c$H;T+GX;1Sr`zYCF*9jTn$Y9j@-)f_si*9tJTk=xldqp7w z)1eppx)RoSG}T$#8ywAcFV^IrF1Yb>7T{AT$yk~5rX*(^8{;Q8TY;p{}Kw`iv*x0BP)zI zQj{9Ll+b&QwjY0~DtEdOh)qA3PNc>5v7~YYUZXNHr|$ejPOSu?E<#BOs|&la4&i6U ztA(;NiwOiS&F4=kQar94q?!wBx>j>f1}3VCQ26$V|I_GHNKl-IhYgR)q49;HhebML zxJJVNzD7cytu4;QZkkMM+(frzSQ|+{(W9|a9c11qgl^0i_g`f_(R(n6M~p1}Ix7I1 zuF)sb@I_Ppr?I&*>LPH>;;{IGzB}_shO_;q#avr##7LZApVi8CYM$8>@QR@UZV&vY zp@i`SbYpuKRhc_$xAivR5m51bQXE^SbdnQX*=%f81A1QhySYx8?{u!7WBx5E<`LzS z5ua;R1y+K3TAU0vF7k09^viu^4Wgp+gk(o~PM!ew38A0TR>BAx1EnHl7Rg?ate#>)9-d-YUW&| z2(Ne`hdWub^Jn-V3|lKy{MaA_a^62w$czJs%QB%IBr28gK07eqcvb`v7a&a8JwUAZ zRiyFMJ^Kxf@_0hk>GG1ykuqUNP(Udh&1b3B(DmpBDzhXIDFv(Aftgk1LVh% zqe(v8>HlZ#-O(IvX0%%2y+@kL!x@7AyOlMVJ>SR69mLW6`a0U%3Dybb5*codC`aKv zNBZ{(4K}EcncfYmjDNQ)+6M?3#Vv_^wZthn^$?<_#Q3hdJB-RcxOHKuy+H^9tB^PW zzFh-sMMNY#6Ef>)z7hR`cc&fOo5)TkVdFHA$2P@U%AlmlQ@yPtjM|_ z)LJwIEK6VBss9m$cmp<>MyFT7nQ3f=zbF+lq6(Rm1z#!!Z0y*kCf29Y94Xlvo?`l# z-!D%7vZWIJadDpn^$OY10zIQbnJ9~egSm4X#j$(>(a1c2=4FBK<{zu}65GbL(bd}4 z@cjCCN%SB>_dF@2ge!rfWF@l^k@_`?#1jDoZh3{FFb=`s;Qg{X>Z)jd;V$?fX1Rks z(?p153rW*}Q^t2;At8$yTs?jack+XQ;MqFJbm0hq;-3B`IJQ`-27IC7h+QkF7e^dh zQ}Q_Snw3JM8T2XXT!=S@fn-^S0u=SKBV6h1d3vpVtyv`5JKk0cmg)F_2wrrr61t|+ zQPE7+G^NL;c_8>+VOfPz-j@u=oBUGREbIeuzbG`dhzQt15p7R znNB`)F7!ZG8`Td4JMy1Rzn`UslTwt3-YgJ!mw*ZH z+WB;yB z%+gLaLlLk(LZsqjIk|X zpHc)ZGP*?b#!?`^r4V`*49aNytb+v8blx1{!0<74!dJ1weymWfUFo|MQcWX28L|Ly zV%b$QLMDiEsa)*Z?omUdFSCC$n;hsWgK0TsjnJJDiBAJe#4{^O`EqIERxD1bGSy_8 z#B0O+XC5I1^oS)w&8>fI8~IVJ{1%DyepR96%0lO7;;{I_e;IXW3zje~ULccQV`&}f ze8jH(@^odaqP~Zv5Ou<(j#3t5Dhk>0Z6OC$0ok*MzGYfxEL0g39c=05S3e520z5)I zkcN;!VEYBXjuaUA41_FfBBP4UP{Ezmgqu~|sE`S#@5*59<^VjEsRl$pmeVIZ}{)wM-Gh@!_cOb5SzNBC^AO59ptjqtnwk>Lm6~CIx zB_h9cHd=jh5dXt>ajtNNBBs{Q#a$ ztOEbQR(3=j4EZxiUMhc|{#Wp8)A3!Hv0qWXwe6z#HQ~26gaQ;nfjuIX9qFTQOIQoD z{upRa1D}Vwn8MR?Swy;l-=yPyP>S`I$wEe#oKYw1;w5+5okQ&u04G%xy^uN8S*b!k zKSVY(A+|Q^(g+?RQ4#SqJ!N0vDEp6itw7}m)x`^zi{21+snfIXA%?TA zvJG_;GE~W*8$f1)#b*9Y@@lKqLz4A(WbdDX1+&1(urcbF!4~nJ%|d5Vt*6ls&zB`| zBKgJQQn?)B#_E>|_fqorUY|l~qVcCx_wFPDXzuT4*`3a;;+3&*G}!EU%{rMGSX@|K zCCf0Kn335OOdHO?gX(gmL2M+*?F zND4|C37<~%R%=cGV0St;hX0<>6bE$i;=~9JwyvnF{*VO96GX;E^`%2(AkCs*`;1%i zu@*A^CVv>JJR&(XJ;muTnxjn=MK1RuQz;FV8EmCv$hq zFYOT{juRO$%`U!GF1z_l#?8MIAQHG6=c7V-S?S^T%zycJmsD9{F5=Ed7P`Dsel-H$ zJq!;W^|8gnBT|?MI$Ri|PQUTfhdvTPY~w>HNbZ1gEjaw!$gOl~o*+hd<%BTS9OWwFH5bi55B*@;!~mre`R z%@R1X0^b`NHPo)Il}wSiIn24NNF&DvDJ-!&+Rl58_oY3x(959oV0Mq5VMN#Gk)>{U zq>G|{E96An6OE6+H-~~7;qxcnDo7S{`jA4Y4Ed)J-hRPl(46mQ{(csT5P#Mip|^LC}W-QYI^!6TS4 z6qQ6>z6Nva7oA@C#=QmtY^4n<&f`th*-~(U@&@|}l}xPW9!cW9-;!UDdmR{+z>uB4 zxcf$cvKWPPZO9v-XHh&*Gcv!KIp!~eTtF)69!>?RnbxKxk{)pwEuv6LD89Eb?8N^P zp$Pm_N%N3oel!z;@J4rDWB*Ip(A)DPF$$;sx6)C2;D0H9nj7Z<{_8D$|1YGdH z|EDLd!Ov*_NBZy4{C_?Ezl{CA8sqJE7AgOG+BdlW@`nxkjP^awOSu_@XDJD2#2sRU zyZA8)tCf$uYUOTh06f|hmNWD=${Z9kjP;<_5Px8ly!p>Y(^nV|uG}^j6qU4*^T;pg zJ2f&-1}vMGiUYLDoV3P8Ry?sq6DiQ6y$Ln&?QuThY&~BG2UY2t+N<4Z$?@qDgvEsngS5rTjx`2&&Q2TUOqw_;&BCin464^cb-{d)h|H8;kUtq^dtprV= z5+Y%W$j+q|h?PeUTU772A2OL{RFov=mvfK1k)+jvB>+svPz$PxE4eE3wLsW-iIBo2 z1%e6%qKbXUWj?$G{G{wQIVWlwX?ZZ22XQ*NM}EF(0mU4T=ImzU0(VysiiEzy-LsCf zyz{rmj8gsHPKtV*=_LrEc2vu;x3~j#03Km}vvi zcpXJ;16AD?3&;){|3!X}T&kiOHzALfXe^@OwHztVl#bdAi#o2<4ujMamocW@VgbK$ zg9qK{4qDzcK%L>^2-)vV~7YjUnC63AiocZzR`CoIEbANRlaN~ablnd z+Ylcz@jeUA*20_%m-10lmUNymezX|I$%e7uw|mp-#Fl+@W2xeyY9 z0nJRvLO<{>xljVH;1n>()0?Vz41H|H6KM%EqAY_Ps*4CJl4csXXP!}rc4H%E^iqjn zo$;UV6WUb84XkKrd8q!w%KnVazRiA(fc76`ZKjYlzK6( z9;7|H67lFoB$!RCiO-jZn!e>_&Bt(fH;0{&!Utkm zT^_G>_hsS>?NL8qj^>hOy6llQMNOvOHY*&EzO^gi0uk*sb2zG+Bu24OoedMM4c*Bb z+=+pw19t-9I*|PI@l>tA8v2o`M84$c!6Y!y9A^oV`8(Jp@VS}m$Lbb^qt}8fL>Jjt zYCPB%bxW;a&LOVjC$+Xr(t|r_n4?{Ftg!F9Rx1vDnn(AeO|Bc$d#LvYYqM{N>#}c& zYs1}cFOEBapCGpkpCq=Bo+!2ypDeZ*pGdUv-??nq-+^pE-w9|!KU0$@zQ!cG$>`65I<713aePu_jpl-$G9l>$xj41O^8lUDCFvEq(EQSO zhS2Pj4obaHqYdZ6pf)P*GrC|*naqMru0QTUzHn@b%lz%Ia=%Od#Gf)gpd109{tqsFctI{)hk!TC)fxI!M4GFo@YK!5)v@xX( z^sQg*&#@x1BmLyw5}pa>QqR)Eb>Z2P+=+H&VB`C~siM!>?%A7yhMEdMZF-oA$kX)H0}4~H*rDhKkyYU}o^Ad`fwlfsfi3jIEaUV;P@3L{-2~JJlq58J zKx~)9!T9b+{ldWJ@KVoO`mvhf=uK|&n^a0v@H|Tz_a2>uo zRKVA`uCTo-0B(UBa6cM2&tTmjc*KFfD|gBO7`P{GDs0hBuOfHK5Lo>yZY*rkJs&;J z#Ew59x9F~aswZtY?9mM$IZnZS?;q}zVK6k$nEszUwz%=IbuYSTI2AkoiJmnXuywDx zOgKnuzK2b1fN~3LUuMI5U9)_W<{;-kiu!8`>jd#8w9^&~O3Mv8*M;~K4VC@qI@GiwQLw;?4MHgABbKt z+S7n@9~j!m_X3ynb)1%NW!ah ziKMo|97&s^^Bej;x0(bV3N=1FMdXWMu=##l)K&bo(_ik{UUMh)=XmZ}qtjW7O_{fW zl?jFOcp3hp370-EFDLm{eya@pXRHUjK^1AZ8j)RuQc-`0O0UBb*uN-eVS*&#kE^)$ zUhrr9h1A181x}%=9Wavg`9l;DR~2$GN+ZFhDF0G| zl&{NUEDqHRQ*tTlso$Fo-spW8WM4|dswUbn{iS7%mdfU>?P(?AyaM}7Ssea3GwcHa zIlhsDy9>TtNnw>a;;6bM)U99;9;M}XIUWRw)o8MFqO*`e-ucx~T;#+g5dsX(I2s0|4JU+G!eO1Do32&dBG@Qy-Qd<)?nh~8gSmnU&SI38V zL@8TbidwBz2?=nuOXSsA^|Es)Q`JmGoz*igWDUD7m~#+VPzm(LXPwm_;PtvW^Qx#~ znX4@k6RZ=5R`lq}7a~OVo=AX1bv*_UoY}nVbjBLc0Qv{>Oqrs(mP~D1g=zWWsp0_# z5Rtr?WN{)pf;WG+|Cv3ha|Bzor~FP#O7n`BG%)+DvO1bT>DXlsoV^(_W@i=8B@Gx=bM7h}H+81y#e z?YI;j;mRecwK4A(c+Vk{oj%6GYv#i+yptd{X2mLY=Hn`rlEuJYTc!NnU{B;5P&&a_ z0g^g&N7%#!uE0Zwt4Ad^;$?V7kSpZ&E=m2z$}l!0@-lj!`HM4j3;UxwO(KRL?|t`3 z=TlN5=iFX&Z*Qndt6p$V_89&%*yB`AI zFH2Wb6pcO^4VC!6;=RP=&odwQ4WYQX|HwmN9E$%9xaR%tSPA>vb1Cu9(&db|t(Nu9 zhkKR497HO3S#^j>cjaFCZ+m!Mh+`_X)u#2Bkzg!Yv1QR*{!?bL)YD(-S-mFcR#68C zw)uLR-{zE^s&ix`3(@?bXPLGWHQ|y9PG%J);X{&}{Dm@TcCpN01}SCz8PDyK42c>4 zR+E28ow%%~lx7D&EpX^9{Tu(!_F4Ya7z|DniPX_8lIbcl>vb@5#9=>ngyi&Cj#`2Q z;_AmUlX@EScFDzvj6gAh<5Ip(kmoFDVtJ=BgUx90`y&Z5Q059+=FfGCXcs}RF?P?> z+Voe+QZGiYsB^JLcr07A{Z213;YjRSx<83GB8J%?}a0 zmfRO1CT&hDK1wwHMQt<@D~^0m^K#FSla+1%=n_b$h_K9Pax>eCX}oJsPSTiMU1wX5 zp0A!M*O+o_wn$Y=bced=o;n_n7Z?qHhG_=Lk|4KJr##B2wzE14HENYx+^+n5*pyjx zGuS;kIH@p#8GkpK-TJhJC|DAe%I7D&&yB=jm)Jhy)zL0qS~yzFb+z}8B!RdJkj zpWWBS!>IO-Zhe^XaGr{35_+#Z+ekBLT}nVdy zUq#+$CsMU#4|uX7fgo>oxn9X=e0-ubycc5ecuptk#&#~#{-$EHtALMn!gQ19rpdy( z5mv4i##){^B3lw62IDH37hG_8Ei#8aqvTP*n6S7x+dNex_-5@qy?8Q@ejkx$6WqT% z4}Q*Q>c%}CGkv<)m^>TfkG9cnq>kJuL8nlELFT~b+2($|3Pfz(Kd6Tp8zw>0nd(G# zc8-m-bj z;CRkjbQ0;kH=FgKH;?d?^AXtu;db_7E^wCUruF2#cs$#(X0d5^!8$*Bp*1y)d`M~T z5R5#lP(-BHeN9CLV+eQYidpCUtT}sM*=%k)i1Ny&`-onur%*cOnk_Z%2i~C5y`Oz; zy+2o93`^r|broz=SXiE&rr9^?*s8ixP-QZ{3Av|{)|}{0I^Ao#eyy~*?F4(#UiI{F zyvxpb-Vzv&!1**uf5T8=D@MNfZnKHLUr~<+s#~<`lx|eM<#Ly~wsfE+J`g_?gT6$y z57U?74sXWlv>Gqv1BV7u|9l;=I|{N(?H7^Mymjw64!ys#sq*pd3`@;^BsJ;jc*~X+ z-SSaeH`i*4)_T3BG&WqJfAj3Q2>aFH^sDDvM+nrONg5J{JFJ8>P!j{u`N1czJvLT0g+^YVj)UZO>1ekE#IqrcW^l4efj*@vbEP)yKfYpMicT9q;hXg z1-6>rsbsu%q4}a`R}z75qBa-gP*I-$e43zy|fULeXR#F36hcposU}Z zkU*^-Wp@HZJn`W2ikj2b53Q*^V_GQ<@gEhu%-t~g7PpoSS#&-3lEJd_Q@aZZyH$E= zDIKMkUbST5C4ahGqG$TDDbv>F&Xswjg7RCnQz_#UBFt-e7EUsaWrzNQ>5Z=S;{}G# zWaIUgg;es*vTEc%EtO$PD`m5@2aIN*HfE19Pnz%B$ML(a7i9 zwIXcz7f*D@fw51|_kIRU%&6)%+p~`v-fYovL@_Cd8=?CmE-U*!bJMd0%ofeoiv8Ti zuSa(GMrbS&3mh-*+1cEA`)?Sm^X;>pc{(d8_k|J=6rtzv7AlyB_3+6s%H;#zFoSUC zEVA3hv`nm+BlPU%jXN!Y=fE&BF$!V-LpQ!wk+sTl1{N~%cI)r~;;8m&9t4M~VrZV` zCpmv;nDu>Nx5Z^?t}MpxyCbswHpOa0PIsd*%hy+l@a~L_JC*f=gLD@fD8MDUORdMJ zOr1L_Ck+kuDVF<$@wI`ncc$_WVXuIzb)WX)(B-(0rA0z#RR~$q5?+V1k9xgu&p#Yd za1b^;9I|amva_AC#wIb+xh{9u&*E^?oNMTDY%dUg6X7|Vr>zo;=Vg3VIviE;YA%1x zPeou0X>-!()HGHNLB$G0{6Z8hz~r14|@`9&KrR5j*OH4Zsv-jum%_Co&Y z!7nHGLQ8ddC;5!=8FcDcyhFcrDHpcPS6BYtrv;L}UI%k8Sit$&YT0{P*@Xp{Rd#rC4!{yYb^V{>c!T&IJ0Q~u-5XFGU>*12ujc#(iM#gR(;%&t75-9&3B zV!O?m%cJ*AN_xKZmE&{jhuywBtjII?bo+7L8`BAwQN4!t@YEz&=quOy0SWfr^7@bl zGw63emM0t~I`=+uiMI|TgPuq8nOY?>RjUR&wp0Dz_447^3+()>R@4TK$+b+omS^(v zgdB7VW>m^-*=~n;ZJ_eaDztr8BO4HjK&vgk8T-5FzR6P{W~h?oj{6J3;|H0`H)V}1 zljL+TOPQ<+=h%~0Dzt;yTPS<+~2smMM?4`Xs%=20F`xIMxLjif~mW$OzuRcWA|V5L`6 z(T<-_7oU|>`^FW32gU0xS?L2uF&LN+E}fIy{Y?t zmdj=y`OjC8uRX)97gX=zJcMlK(&e`CJnp&X4Ook(&&MY3W-5lJcSp!k?88H^>oQ%t zb*0JH#q6ckd!yh=oeRflo7FJ`uduNf*SbPmeJscyHUhpy)Xq4*1msnVq) z;j2(aSGH{u!HRT7UyQ7BQ=(UD&A2U_vh|^fuc+eQc80CtHtUh- zbQ6z&eb)S?h&?@Qa4VdQ*RgKLJt<_NON}yK zAl&Gwx- zD=3!1^BP*@cdb9X&r{KO*!rcmEA$$3QSG4kNW-rx!@~6;a>%p;%jEA{T=9B4v!O{W zE)7Yagv>pRj9VgS51`#L&_3~c>gO+J&cll-9v5%yS6b#3ru*^Nw8$YZZ^dU<`P{KN2W^VdOPMvj-S!HPq#xb8LPkFZ3aq=0Qe=xED4z1z&r zWcvj*G>XS&*3zwqNHiD+JC{ZM;dyP^y*$l=vIx!Jt$PExX0!332&faCmea>|IiL*` zsH;2ui+yz+=dVZZgQ4)K@+B#sB)R>`=jdyqEg$8Y7jM&c>ZHsabXDP_T}?lhy*+e_n*R4Ie9!=^`< zj0P9SCHz+;OLGs-Bqx3A`)Der?DMw9RQE3X+f6@>et8~f?H4)&iQBEhI1qH9kjy!J-={aeJ2u^ViPs-5L}{qm?3Hkp=-yH;ir{bPyg<#8{}g!ZZ8dfFR}rs)H&Wd!WWSRBYo?Qht}t}`~ym6J-LwL1&CWB#HjEN0W-9yMyygPRYn<0=?r6vJ04D->qk#FcS;|zVSB`CLP6>+ z_3KfWBjB5l0u?u_-@o*J{S#&U9T8^%x(7t$I>FnYS?g<_=$hj#$8}Szyawcy-7BG{)Q>~xRUt9$vWzQ%bC=H`)o-dqCS#L4bsY0>-w!4wmhs;#{ zgkk$QI~Rt?C?rv+%zPQ8_S+cG6J*mha{M=K3I4A+oz5<3<^4+~bh8%G3OS2tp!{pg zg}CR2kFw&+lyJPdQ(Xc5Z{lei#kRF7pMF}uDn)%K}}fQC}n?V)`?Z1B|t+@yr0SJQ598Q9^oBScxJxG{(E@NjqA zlBQ?=Ms2H`*ZRe0nY!cUw0+e09vE}#LbsXsBzyUq0}0aw9qw1%qwm*aQA@T-H>{Nd z^)FP@ihU7m;A?m2M;91WC_C1AeGSdCGEfvPrcD?aGRe|aKD)8Ki&F&)KXNCEEW(!- zd%pT2j5kY;z2<`Qg3QMsj=dMN6e%ZD-?cs1tGI;7`aj}L29~O9C6k%AS%+w{BnWOt zXq0UY?tu1=uY@#>xJ0dn^KFaBiY~1%i|m)2ry-Hi(5sJlJCUbR3U5_Z$3hb~ipO z7|5sgylug!HPSYu_H80_V^i;ag=Wx$N5}R1Ixn5>BMFT5i71M$!zCu}-OI=_3zIAi zPP3b;M5*?&AuVxt7S=-J`(5Tl=Anb_@taQCxq8;~qwwg@s>ag_?j^HHFxwV~dV{6p z*J1<;6-hhyxZ}~Ni*)41!$XPY;ku32y93Cwr1DQnuPM!Gv^l0Og^|Hc2gYEK>M9T$ zmo^=xj|(}QaYPi|CPSfjlG|vT$TmlV{#+Op^&`@3SeaPot4tn8!S4t@4w)m70a7M5 zE8|Snr?>DSXWK;WVJ-5|j(r99iIU|AhSR;S-e7kX0C+`BwHF1(R{1FHnXAB0Hj&Nh z>kxe=R-_L7`;v?Ixm}g@n<%aHgLflE$MI`7mbk7V7IJP|X{wA3n}@doZRl{wxbHSb z&ecn=*>|$|3;GUvOdISE0(JP5DKGp`~G?)2N_Cd_^GY>*3A9V?jp@M+8G`A?2aVonp6trilV=7vSFa!N*H8ZeQb4W0-u4RnYBO0`^Gk{TT0h9M<82E7u2uw~^}p(` zJ)^te8f_mu9y~}<$HjI`mpg6!Y|pf>k@UWTy|CWYk&UKaSs5H0oaW`U&~EXc8~*fN zw`=FCKik`T9`;#v!!A5#L`?XOMGL2QzJ6lz`>)$FI>mH%^qbK$YRlPYV|IMVPj`}^ z9DfGW}6gmup8U5!*Vn=buhYr5@YOf)Tdng6{#-(+pJSN`*Ji1+as zeGgTh7&%2b{OfqTjK(Jxo@%l`KDE)e&CZe|t*Tb}KB3Ff(*+G%6gZ7;JQmCjYuByz z1K(9Me4PIn+#%}K%SD}%Wj!|Md+b&>%i5aia;DHF({`pR!MAv_=b1`LX&yrYYOZR2 zCAR*WIrBpH*at538+7^Z$U!wa1jm0FE?St^bw{7V_>nJTD%muxR&QQ$Qk08h`0`~f zV~(vpav21y>lAeV+Ok!NHs?K8)fhafk4wDokRC^xyX9=I{)wHodVDPp;oVmQmTr9W z*ReK@-J?F7j~li6Kw6)HBfXaISkg9fV&!svD=Ng@C~&LY<6zXKtT&P`^G^)h5^?V^ zHg=57-1NkwM>|B7yLH_&zHg1K$7Zo=@w&BCiV}~GUQ(s4)qu4Rd$gLJz&$AnoVLG7 z`M@DZ!v=cXbL~E|8vE;>=TF}sJFxFy>cu&K4U(^m_CFSNs{aO;6|P?&%Ad}tdw(w0 zKV`w{&rj2*57=8pSsiyjh=N@rZe7iMz(~@xuGp?WMe8{T%Bl-Q>1Dn15gA?~{ ziAA5p740ie4CKuk;(6_=N({r z+&t*x(APyxa>p@*uQG5(}+|NxW-5dxsy|8ST8%@ zS1osC#o5bLDk>T^;(h4Tr?M|^{`~II=2c%`heHj#TW*_{a3m_V>AWX1W}GZPH=8(AcAvxB*|RHd=$){;^9JXa!$0i2 z)z88YOW9Cc-tJ?qrV{HvhB${6N6qwU_Bz&5X&;tT{h;-o)TtGRI&V1mCMlw}-(?4j z7VbmG%*CW+ZDs`UoabV3#D&ejnvgXs%7RLEj4fD!yo%vKUP*Csemf3rH z=dT~$=yk!J+2?lmxn>o><{j;T9aA>8_dM*HH-uBkeO2uSr$@A1k&*Fr#<%#Zme+c@ zWxn?a6u-JYxYwb^J?AAYzu)_y>PmNI!n|X>-!8vWl==R_`P?lFKHJ%Wj*YtD59+2i zd-Z4Tp>K&9aI1amhx`f^FKXso$?ZPfjc<2l$6ZUhvM; zqT88syT7mt9G!cgzN@~Rv(^z-C=R{LVkh!%~s;@X6I9%4jaET?A6V3 zE^YtX8e_3}=$pKr9rtBcuef6I=mn=@3pQ1F7BfUq#T#C_w&mQo9sAky4vtk+Jvn>l z%QMY-JFwj2dY&4)ZqlYBTLS)CuHod=>ChTGTWRJ6@9G<-);?Ov(#zVr|GN6z?pB^Y z?p5UcJ@cAZXn^NW8@6HVz}uD2sO)0?It>>sO4;PX;{P9S?-=FFlWmLss%n>Q+ctOE z+-2Lgc43!o+qP}nwrv}4{7?6}?{uGc?;YcfKK&&}X2u%%VXYZ4b4Eta5KhC`Rimo# zUY!fsX$2+hWX>8(!7R;FbIphKi54+w(YNpm3J+!VoN*h91OrP8mk7E6tBT?SllO%t z-nGqPG!0AT((HtF-6)8;xy3+*0*R`LbQwE=z?${A@i=gHHn1zprw*?o3=?Om+MQ}! z7GWG~`Q`uA5;Rar)TuWC^=3xUizAXJ0M%lj5QUn)m#Ng8EoV+QXlkRNo+66~bBNOE zgoOkth$RL5x}nLeT{5V%F65GWjoF^oI-fDZ%`~sPI-l)Lh0| zoJtvtvYae-T@eepn8v!KBX&QapgTG7FO!ktt!An2(<`~nH@s8 z>fyfxTqAmwylnf{37S*vQjnmOY$lT>p%m}9Z2EWEx?&h8PlcuII@zgsSu z0Q5yeHp~i+aUP@b;!R3iZ_!12?II4s8Fz&Shshe3-`pY~DI~UMV52}w+E2VGMWAHN zmg=@yUuosK!1fK?&?`E$kkJrRR%4*Hbf|p0ej;@#<;Sn4!@Bfow;(D%WTDsU5s-qJ zk`wl=W6WsI%wm~M=m-b_@!I_aa7|AbNQ)|lQ!vpwhBO7HSUf?)O%Qv!FFRitNu*WjL?ztK%|1ml=G5 z7u{_h-J9;N_gg=FEicce9q?p)?~Wa5nQq@&H7ZNUZL4+`tTW>V*hZT1GIgj)R0AG&RoB?cSthu?(F)3aq4DnUisXwZxY6b7^AJ_d z+Zy4@s>P&HtJ~F!tTpN-f|XR1T#L#^*~);iwdUDCc~_zOI#8xa{hZ1v>D(V9w<|@b zh>erW+4N;^HI5R*y_i|3_%koD=~n^zBB##QYPohcfs00%TX)LngW&OR_lkw)R*Dt3>Mh6{4=FAW;9uwwXK;%*lD~g63nnX z;{Z#c^CHy|(zlKp$h8S(m~$eZT4Csx$ zvSe6PE)q)Gh3~u_CCZ4-lJ$&8hJw6zvVpoVNR5LH;;+8Rd4Jt?At@4zJyF#kNrlk+ z#&q`aNGOcM?ik%p@6VUlF-~>cC`)NeNJ)G#Ko-1+s-9udKj4l9sAJIA?$|h9SHd0z z^89JyU6%#x8M~&9Jlj#a(r#=_ZKI2%wxW}Ebd*({*6&g&1wudg*KbARU8sNgdr|+Q zkVc{Wyi9-g%%#fBEAV4EStNi~no8Ma=FUkgOjLp0lB@-T$D1H0qUaK+gR_t;_a8$@ zujTxs8@Bl6;V=B!={4=Z%drhBzca<^$xfaYksLmXCxXI`E{x9rq2P|-hS7TzBk5+@ zUWby)Ea2sg#N8<7g)?K zntMwS#n@cGlazZ3b#cU4J>+GC)sUcpP2R);#U;(y#glaA8DwFlO=Hi8rG!6VS@Ro# z9)&3|R?+15M4ixkE#uhMMLDAJ}oG$^4Rj) zZp9mmi$P$@la7Tz>^NO$k9)3u5`qNPG}zfJ>0-*qc*sTSEK31q77q{OYMYV!(<6=R zCwF9#?j6pvzwWzTT!WiJ@UaF557>+5E05CliN;IDLaGi*JA|Bx zOr-6}CRCc+VKeo|wm$BvgxEpPt$VJYO4I8|b&n61`rgJKbZjcgMVhxY<18+N!r{`C z41)V}`JI~wyWon`_18aI#7MM!ltQuHlS0{gwZwEAP?pFxd8Z1Ff1HQVuh}`JFD@=uvk;UsitX!Drk~BGfkdFPmRv60hkf)Ap zYiuW>I<8nD^8>C>1A^naHRo`X4{t~Qs(V{gD$17jJ{&Fe(vq0y=1Jx`THgWN?3|AZ z1XtWKi0qEqLVWvoemQMzQ!l#Mvle!zh~uvFiR;fzQT5p=!@_3|a60W78M&n=n_xta zB)G4~ri(c(9;OTjYg6vFkx+0l-h#P+fWEl&I1DnRA|!W-`l=OQ9mBd6w9F*qoBj$r z&bLPH@nTg=#(K?;eWF;`wo%NHj7yKYMv|9{5}5e)p)dTn5(yg-3k|mLTpFc_Vyz7? zsj^@pDp1w66mm}|Te%pQ4rh_tBAQc}i&g{AI_Fc5BDW6`b(4Hs^6}ewWu~gq19b;E zDN&$imxXE4V^!7?FVa`G%<7fzVh zw&C>g5Yvqb!elKB&84}(?mv>d!8cV@F19Y4?<{SY30GkRFm*QjV) z>#ICYEIgMvQ!Ba2vaF|wfX~%)2we~~tYHxo(EIAQ5+!KWfU1DRr%{|kUc4=?0I@-= zNH`<>kUaWkI5XA77~n|;!uJVh*IqOg!ZoZxkF?2wDvEEuf0S!PBy~$ZU;qFi4glc% z?~!Z5#wPmCR!$O5#@3F1({7t;`nGH02%l5cDS0O_@{Fx2Wo2vxvoT61SV*9ACk63& zgFlBfm)e)Eu==|BDm%fD83?WCv*^aa$91kJI-ZZ*lIB$_RW4tQzb%q!tFya6Y=y*Cyx$*P~dgWGyUMTAB;gs3=;L50%3 z6alF*&{0ldMtz7Q!bMRP`Sf zMrl2%C<&`;T+Y`ejNJIy5`n~A?+H+2v0fsDsVHk*1?hNBTsv-$HbGyfUyGahLGe8t z25)}$7tG_8O5;V>-|^qJC$rW^d~39-hvx60c1=xBqV{f`{v5GmKK<2--gRT{mwPfA#Tked16ud71xs_-+omRZc^0Y#m=X1kNLE4G17n~=LS&@< zLqmYpN7Z{A^$j<7jf71>&`-Fw%VLz-NwoIO-(w1uL$1pnzrLa%snFY!Nr<_qKlsQL zaYB5GQJkM&TK-&+8@w^LrwmAV-O}2-QY^j(>gTqn#C;fy_02yKGEylUFl?RS+(cn< zA{<^PS|a5uJmj1tFk_NGS?&TmpRrZu2YFmWBhDvC+mk4w?!5X7=o4Bk?SAxD>Q*)~ zZfI|Qw6tk&U_lfgK#x+E$wbim`Z_#%}n$kf|8ML}j1aNjEuN>IrR z9DV=&?FfQEXxC2U|@-KfV6-dL8`>C z$PnkQxyzSRx_QGn0ilLJO!%>M*fPJMKNQ6AA@_wj8d-=zr*dwYg_QliF=MYb%I^)7 z6LhcSdQ;1rD6IX8iN!r25q-50InJ_ABx7pbHt&ZdY~<^dP$P>jHl7#%^yr<=+i$H* z`VHaMF%xT*pThFs&4uZ^;m7U6^Cq}pZNCSS|G=d3ua&L~hL>(YI?636@QSuq?nZhW zG-A}62b?t?$^a7k>f=V3K!H(^QBXu`eo(rUtupGEvCGF50V$lrKW(jHH{Gms$UU$`n& z%lkzZwwgUb((x$k=>uJQq1?);sQuO$9pXHIE4&irG3MuT)DT@*jC~jQ#>l?Z+(cZ7 zwA7=SfTx~Ldr(_>>DsWl!M$rf2)^vX0cpa?YDYqQ7^!D35(UnO7r z%4L;Hzl)&inn_EazDCGk1;;~mAIfR_8_X$KNA0BHUHqQw8^ob?y&2|C*tSs5Gs z{m<6FGvB4Qwd{Ti+SgR|jS4YiMA8iUTT_Sl1p{-lMv#Ed)ydhJ#UWYIW88rHdTvek z*O>Vlne`e;H2E@RJ+dUySWmr_C|eyE#FO<8z3dkpPw$t_;U>@=OJ71y0dT!HLxmrlb$}m#%y{4N7($;- z05gsG4|{7ER!*@Nv&DJjww!7jSqsBcOqm zlBg(dGv0d263}(RzMp69;#&S7POzNP{K*R>_;rQxuD_jFY(GCpO1pO`LF~ScY4>y~ zon}I2j=d3wVjojE@R|^f3C-!#MHfr(mLuKk)hn?ZE#5y!#;tdev4{Prr{CdpYxjS~ zH(M&_yL;6=T;6W#zoax`$-Xz7x71ctCa=I1Q&`NG;3>|B!17!+x$VQ_w4oI&A<;PJ zqcx_^xu@Q8!Z_!nWk-`<#*yt!1$~6d1y?fTj@v{zJ6*Tq{46mgOx`DtIAMqC%(Xih zN3F{AOuj`YzCsy<8fUcitTD(kTqp|%(X`@XyVc{-ZJqHlOxesqHNPrxr@g`7^da8hibsu^aom739SikDDhaCp9zwViZx#YX!!*4jV)S- zB@NXMgwx=*yio--!wOe{7E)odgq*(&vvKH&a53Jbluo<@Afa`61uWl30$NK2BtL1U zo;_!GhskIi>*BxZG021x#+MX z&9aGAf44!;5Rdzqu_d$GE&+MY6~S>xn-yh?z2p9T@%z?eu`Fi={RQrdh;hfN&jM$9 zmqlh!HE#B5IJ!=;cbIBGq_^V-yhG%?-b)PE2wXJ_v8w0_>}05Fb^Lbbl9spctXX%( zY-LwawYA^|P{EPin+-lPdU`D51l>fln|2Gs<;MNQQfcH=7KPv+6HpoQ>ii@MlOril zQ6xGMdBNqpAF>tFU+&hKMLcch!jOW)iN~RaX}@DEhax&^iRKIk7?WQM&C-q%5W~;y z$yjoDS|Mb0p5(eZz_wa`yBMM;&TJ-cV!7TH{QWL|h1`!!$EA-23u5hQ}fytUqXR0|`7nz-7hJ_G4)EuuVR7ZUUhK}=%rfSE& zO$?-$CZJ-C)`%l=614E}JzN=)1WCdKu~K$4gwUPaZ-NtBAZWN+!4<0&-YC}88{~b& z1U<2I+h~3D$;PJUy~8has3q_gVwG9bfSFv#^iL6k1>CvpLbm}7=bv{O2zY9S@H-!I zOQ8lC&V{PfYK?&eTj~0S?Yi56`b5!b)LoFZpp&Hf%Lu9{s_hV`9$oq!Z*sa-hNbbb zREg=}L0|(b5vU0x_0&CduFA=zvV%fu&M&BdST$~s&O5bK;tN~%9%(hu0A^!NKpp9q zx4|+Iu}kC%Gr3Oe>-UHFlMt91PP4lOKayx^Ue6q@YT70qbnU0zWpj8~3ExQafle~n z&~$rs8dVyVhfLy6N#V$I5mNv9@w~;CIaPUtqSwOL$6qgr{pZVsYg)$A0-7D36in5%eQG( z3(XQrtD2us!D<&0l7EwRiwniFWK2-On6I{U&b5Wnw$F4`|Ir1_Y-VvrNn=Q3ph6IR z?F*^0a7GNfC^16i6M6nTZQL00+F>j}=w6i5(yHrZHLC=?JPMj-TM>3p1ecDvNj>Vjuoh3kcEJz2tj*0|$=3Y~PiMw)oKCNkz!{>R(ai0c#RK>e_7 zPRiaIwU9&PR|ZodhdH<`?Vv>bUcIvQ z3w<3Lw!APQC=spwNwj}0j$@9hnPZNslw*!c0Y`ljlQycO-PV-0lVeUn9!LHlgg_A| z)0sa4c>jjlJ-i&6##M@hfPA2_vwm zA|+5hrLR<m0XH*gEoN9+y7%uqJAX3t|B~(+s1xvUJs|s>%L0J%2 zoRA{rC?V~cH3=sQ9Ry1u6mYvVFq^V8Fjd{em^AR1hP5-CoCcg+dzZ@}P$sNG2_TCu zvo4Ar^h0JJnMEd_FsouCV}P-B;S&>33STWb8Gt1Mi-M@r6e%p{m^C6zP`xWiem#Z0 zPc&c0Gl_m)%_)h#vq5fGT8AstC4NTp^4?hD#CUwinl>@vjX`;uVifxH4p}Np*d;ZT zR2|4`Ikd3z-NE$oxjuE)t6Qp@UY|}PUL?LSMI=u+l*ElBHeFW12%VHVn`RKyCfXMB_6HsU=AMU`+(DHa2ysGaM zCr}QcnWj+)j;_gD#_Wh2RR$~rM~b%m6I*;mqsg^2-u(fAgit9EC|dlrCv3YLAHk@I zn(=K&{GlO$HuOTIDnnlr>>EVQ=?`v1D1sY=!2#d{NC>J=tcK^DR~(q}06L6(kU#or z4Om2`8>=kjm~gkf?pae;FS(nmF?@(;0kYOd(Y@eWi<*sP1g}P6eX_lNASmAi7542W z?Z91z=s&w0-wzp=h&YsN0}8gvmg@*0*R0VcSl2UtTsDOjtZ`f#2_QABlf`K?yQdwV zMyy+-N)*qQnf`T?D$b>Sam0_rEIG)ih|35rny6p|60kT4%rA!ZU7mic3x^K0o_B|LrYlWCWd>vnyu=$Lt)F^F(FXCcJDN_V&Jx;*xq3#_QsJ;Qyn< zV|B4RsR9B3=0N}elYhCy`@7HLUlxH5)_+@Q`nT1X3i_7o3<#fBy7vA)mrT@`7!^N| z&C;7NjtXe>4HKA9&XK5NosUSGU$)_gi&zR@)&3w|pKi8$6hBDC{V>h;@Y6zwI}T)qqZno1NE-*nyi2Q{Gr$6^J)P4YLK+~394%W zj%|&AWjVuXRthu<;UPs2wupAPMliz4iWhW7Q0b?@YY3<=)=R)c6;~TK*j|v6+l2C& zKpTh%49-h%35VnEWc)Kb#83JRvDRH$RGyYGpIJ}NK!S?U|E9O4&@ab)S#tx&CXM=n zl$%o)r$Q>Kd4cDu#@3G73%f{rZhcBfWpKje|I?!4Y~GjJaEJe=p3}&5$v|;6gG7@OQrFir}pst?+M`XB+BVsTtDXVc)bbF5jH=FL~iiu6p zW>mxD^%|B)G{_xGD$I_olZ~?EI%jqTPOvWFg(w_6YzE889)9=uH{fCX75*O)BU6^d zY6k=W+yMgszyCe+|&{$;YLf+f3fMZ}*+ zS}ID5kSH#nJk-T-wbGSz>3jFQ2$0FYa!h`9cEXEKZ$rc1Y8se38%T8(sk+KO7<#CC zj8$|yTKK6#HlD&^LYQ^Tr*e6Aj$5N<*yz>g&}oM6mFL7}Xo{;35#=xx3SJ_70)mg5 z%2QfNTtW*EIzD;a%LRf8!{toin>-h)L%b7(o12#z+OE;4V95Muo20s35uH(w8w+hn0{m+wt5=qrWMH6MJUK$!AbJjw#iQD6D0i~4u*wjl}>Jok_MutNeySGZ$)(LCu>e$lR0TW22R_+eol_S zsE)C@Zng4N_8_nJBTtJLz4bLbe?h?5Na;VCMIH35ja_XWEdLV=DcX+tY$!q3pVU((O!Y3<@jiU)&PeojRb6_e%6Jx z9lF@%3=kN}E9%5w5r-MX_}3WTh(Ssqd5dp`Z|isVuXeBu9)5@OSZK2K!>`!{+C+L% z+%SZXH3&7PgalE$fpwX|nWhnJcwi0sxdDS`j^f!$%kr7XUxlI^8~i z71{-?kC+AXR4r|<3*w(4V3MDbZNypNyzagH2eTFf@F@r+V>Fn*VK{p@td#C}pgDhJ zeEM5c>W!zu&kgD^Z;LIQx(Do0aA0|4Y+_d~;X*%UaJM6^xq#;d^=1Sv2*fd1^eo)- z%Ruj_>3xOa=G%X^ZLX_W-sP?bI!aUW z(*F3;?%V6TM(l^boJ$g&5nO3ewC>fxWA}%6R5~C7<%TT3H-)XyYBedQ!lSG^@rlg; zOkS}e^NFOeVz9%pWxc>aKxKc$0^cZ;E}KI^C=$r=TUf{+_yRH2N6;;=brPb#UY?~d z5|CS2n;p6n1mDiURG%1Rp8(Z=I9g<>n$i;$-pTz$dX-Si(4Lgx@f(=L9Go-3$tRG@ zPkv-dL4`4)^jBy0SDpoDeFpKzs!ZK=!e7vW6#D#ALxoYH09I}iq!Wl{aRm;>s=?fE zPWxBerUuN|drnaO!&%)@NB><%zvq_zrJLBHo7ihd{@Q)i_IYr&89lBCoBJ zo73JopKAmc30Ix%)YQ5+smj6i`8mn8KNErHCDGf!^*3xR`P%U%2~Fs!R*#sYQA(2i zW9>X=`LnsVvW^RP)*`Mhc`13aSg|hmU1B}Jv6UV#v{W_Z0(d`mwokzTF2!*O$Qz!4 zJY@~Tg{}wzN-!HpUN6hv&8e>0Rk&?+#Nw$izaym#2|=!-pW6W&tU?>6dIZ}3090@~ z6z*jN;Uz$nx^sw?I^uBpX!p%7k9uR<{7ebzk2IG{IG*5`4jo z<)WC(0LCQ(m`V9CmSUW{mx>8~-F!JpSRbzWO4}2l>Djp{%3}I&o)a8fA3oCF3hEME zS|?NqDDkxNW);e_)2@aO4;X>pFr6k0VD1qclgrpFKC{cNLV-pYpJZ+SafYMMG9M}M z`kB-}vhDEcyNVE6NIr_2{pq_1F)Bwdb2Ynx&*q<-EEHsqr!9v{0LUoW*fN4JDZa%~rWzUQuzuiaxYCoj$%<$Ne&wezjyP3bd zYFm#?KK|NR>1{ppN{a8?r;pnI># zG|TNYvSGeYVZYyMFm=}c^LxD{NmS%l$K>SMQSNF20*A@mv&s>zSaKQyXF-{J$5)d^ z#g$2qXXN4=(D<{Z!H6q3;}sbTA@Qp946j$w?f z(k=#7+(ZH4tVAOrmHw$g1o@PkTzojQJOc^L~AEuVju%EsSr%e z3H&ZrN;u@hU+LG1sLKe5K&|84`5Kli^6xU*BX$|(KSvCr^YED03m(#3yGh~8OxRDb zRh5IenFy5foC%zCbmj zVszGS$we-VABLVwd&y$b^hY=gQV}{wg-)>sf0<2X)y*VGU&o)&xb{ix2P%aLlF`!9 z)6on6F04FNUyXx#?@quf_C1a0Z}R5eGU}jB zv?tu5f}{Ki`j4QQ#+Td%0|5YRAOV2j|9Q};{`Fi}M@1vcf8y7F29ff$?K&I6XI1y! z;)$Mzk9oU#Je{2%Yd4f(4f4Q{p8*D#2DfGfr2;`I#+oi)i~`E^dRX4U=l>6=t1HAxYQMxtZ6RHO~0r8O&*P}JK%qMJZU9;$7z|6LwMh!mEc@;va9bBBo8pBr_pAbMxGbsYL!6jY z9w5doKDBBCdx?~^P?lYCXShjA2RE36tZq|{CzPd>KZ0%d%Po^gl8Z_zw2bjG8^UMU zH&PU!uL@^j@Dl|;F7-xZ|B&?RXbF9A z%{-QjHSU?{F;fN|03;C;OT|c8LG+z8MXOY_%Xz?{zKXLrV0(=5sVkzjuxN}Bys4Le zY96Q8&(B;o*}DX}v@7q6Ob9vpe=1X+RIuXI0AYV|y%>*x(Q+Se-#SgIE(}}`!bWSR zh|}IPW$R2gBVL2>e}l)o+Btn(AuussHI8Ov;Nas5k_~m@rNV#X?-_v`ENr)3bHCF6 z?7F<${vg6?0i9%hA0+ndVYVWW$Fog{%Fz}Ab81v4jngu*^$t|9zg z&ryKJbjV9{6d}juzVf<5fA zFtC39t~ry>H1R&6p6so9e*UKouJan7^V!pT+4@zThkHV-=#2gf7axaxUpL0OaqzIz zXWmQr4UWsI-U43(;m@_lZlR9M2Xx3!+`=|p{9JkO_lFPYf9$H6OX>KMzyJUaH~`@I z?~y@&+d@d%+~z-Rd=hkJ*BMX-x9=(G5Eb+la2h&un~>yAs06~z3eAy_95UC3!uKs- z;c4C<;^(7G7@+T*GABhy z#o3y=&W}2ILNTQEIyL#EubMeL;(AizRul+AXyiK1$N_8+sbplK!ddSM1$U&N@NABb z1!br;5_LZOIgYow!dZMG#Yo{CvAAx_FzX6 zMQHLv6~(EylVoy7n$j#9SIx-`zgEw~Ry_$ezQDV^74)Mb(Z#>vVJqXP6`6EtqxP+} z((kOJM@-3R=BUxqe}peP!&4$y0vG`rv!M%96Ut--Y?W_>DFxin>Y}V7GEUHar4uGb zqhyD~JcfFw&3E(XSqTg}sxI*e#xxYIg)gZ8Z(p=~aK=@np*h#CE&}b5lzW0pT)a({ zbJL;zUypVI&N6Q;;NT!9k8`=rK<}^ZiR&I94?m3!AmpW&3~MWIkL;ysy%9Ax)*;p+ z(T}zp>mTuQJDt)eicqZ5xy9k5o9^o=3ftqL#F*f~R*zC@J%+AaUz?elcB&k#Xg>d} z-!xCN3uPUB!(V-Hu*tXudKIofd(wO^*LcEr$zMQwP4fBa-hcK?dx3_#nw@NCdMmb~ zGat{<`V;$U2%+X*bB&CEb)b3;Y6+yEjv2z% zkxC&G!4BLSByhuZ3_|p5(}q1+?p8fQ@Ym&Pt=N|?<1UJ%wRUR8>u?57I&M~b?!{RP zY&r0T($TT6_jsOFDEOOi@3HvZ;^dJ+i^dOz#;P22d+n6LI15}|1IM0MN^Wp!4g2&bPNMl2S#H-2au%88bIceM7WC2GAP2}PAkQp zopEH)hT~AGhP0XXBnj;a-&HAVXFuz7pJi`*N38h+(O3r^Fn}A+@c6m0%j)%J-6_!` z{~nX~f%FBXLJMd>7=V(<^gR$1QFnF9@i^Brm4fMvzDU>6A3I~?Jj7HZ9?}CDJYlaK zrx|<^cwshy>Z_>v*;~_KSh^<;;@?}q>aKe%qXF3UkE5A^_b<$_E)upG0Vs0 zAYGTf2SW(BPy0~A6h55aR4;n8LxYk}cs+rk!Y-1+n0|1nduWz4YDYQj^MK%tkRk>} zh2W5~G3t_S^*h-V)KOUfae|FhMXB`pRe8c3@S(q0?Mc?@hk+NdM>DhhEeBSY8iP4c zvi=Z%g&aX{LD&ri3-?8=9`+Vv$sEo6r4=3{C?dX=nE z-p*$E7EC5BMJ=i*J*y>hNS5MWV4*=TQqK<>*o5NMK!@ouR4TjcHy}$hB5Ja9o!>B#*JcQaA*W#266+Qq^LT;!g zBOPw?-IIaY2lN;@taMsa2OAz&&&R4>@kJ}|X&iDO$ZBGCvd^S=Bo2zH=%NQfD1ATV zVe$gb3WPywL4)tIg}Yh6)Q&+kw&xc<32*0E)$zjb#yD|8j*gAP>RmMrvW~w?9E#R* zh=QKD`W>mcCM1JdHY@Iq1J^5jIvB8;sO&T72>8So(w?$(o3~tUTjKR7TCNO*&JPYM z!^Kp`V|IS6gj|ZeJI|#??y`+HxI024gP3;JFJ3)u8Apa?%_7>w~wfx`UEiJgNA)gs{F+d=w{zOXIj(M0D#QstM( zIm;Wzoa{(le~$h`3AM0=NPd24~NWwo) zjsC)GsuJC&GNF%7vMUOz27Gi@{_?f%UZZ(fRU#~GGv=oTt*lP)lUYUp=~$`m7tqRG zvgbd|cABi0Gw%ZcfJPtyAnae9UjcV#2V(_eJKO(nZv5Xy{w?2$(E`8v`C&w!c?OH$ zrRf+d2Y@9D{HW5Pg)xKSjD}`c6oFI?dt+OGCFkT@Asy>y0NRf; zG}hi;T;5ZWdFFfSZr^(HN(|Ob#mWz7rEJ`1FSyf_`jHNmTS`*+RTZM5iZGYaLz_BI zY{$EYz5dAuuOH1?W*qF|BsRE%97($U>M8CciJeug-3v_aU1s6q<@unxzucxa&2xaVdf;@B3$ZDW_ zJJC?HNZy**(4tQ?6+*~E3=l{cOi8guH0?^hBp##;eY81p_Optv;6tCQLhlq*%BOe5 zS;C(wr?#v^YqKBa4<#c7L|`_#{Qr=1O^64xY+Bzn4>qp@+{{@nl=nEa`MnZKU~yNr z@m7D;w#HtrW|3k@Za>r(T03a){d&mAxe?`}0;kf*VpuOIf9IEAL$2cs;eI#%{-?1f zl?}uv5*Pp=1OotU|2@J-=&wy#?EWr#l=N-a7!W?QbnPRlK*1Tx*;*g_>kz=_8%UmLo{DEEgF?NF(1ECf#?J0ny`--_+cbK*4MMi5FS{qwi;-p#wnEUPoH#SGm z7LJ#@@$7v;)-^VuszRUR6T~m6n5{l4zk?;65XaM}_`2qh-4!U%TW?225E^=+!++F#S@gBQ-@kVj2rz4_0M z?u&8xX~USyQ&AR_FZXe@-_uFpqOm?yCY;pW!hc(JD!12UmNNK-`hEk+y~{-RWyZA~ zFRLnJy7ZqeanT6sT{rFnQ3m$G8|;HGm=$oil4NEGc_zP2b53Are@wPAan~3OO(Jb} z?t|-`lxBHV@;j7U@oHcyDJ^TIB za_K)z=v}-(0Ki|p!}@=|XUORrTKkzicqRd;fwT$tc$=0U2d`S6sq8bJ&=Pg_Ec(L;CV{(-bKP5*zk=~3e0^@Oe50KgSfs2iX z4kl`7SUC(FA@EzGA!7L?4g;)>f;W&*ev)_6Rot@BMOUQqBnomnVC1bfVxpfR?YlA@ zZ&{|oz9T7G+GCIlOQJj%jC<3UQ&`wP-+BD#%6vTi>X_u*P*GN(osi%^R?L0q1=ew$OK3HR)B%t-F9MJxpJ1|8W8((_DXK8=s-SuRpo z5%JC%aQvhF$#r&H9^SD1lZ)@H6Hz;%BsRLys{4ba+sn$_>k;i=B*@m{>lANH?c*w| z4nG)lg_@>zO?Y%^yZp)n10!P~bR-(kH12v}`c!7~2kHb>#DA#7xY{{>_d=@!E0Qi; z01>a?+r$8EK$5?3()bGWzARbp6s-TyE9AiR-vFvkEt~Pn_s@+Mlr&pG6KDZpnKi3Z z2^fvyN22zHLD{@X>Hz9&&kc;s1=EV`DK$@_GNeQa+C$Kstd6H>Bih80UxfShLirWIl?4yC~WmVjmymO6{M^bUz(kewJ?Nfu3)jSlQ zLLy%Rd;K&9ZhD&DeI7#36J0sS(M}Q*kHyhE6?qnRRW5XkNR10vZ zniT|CRCXU;4wRjXlecv~_<5NrU1W_jx@?pP(z^84K!Fcf-u~%>!{nOE&oOWSpppmx z2>KTy{Xah~2pc*^FNAgku6ge|o21<17d0iNDf zFo6rUca@nrilbWLvlu0u@Atu&HuWUKcA4<1ZKBwI+pBFkOdaZ1cCe#|Rc4>v!rlWM zV!D=H1y2Pgsoi{(+|T<0kWK%sX6W7`#w`cLu|Qri9(JZrl=0Jq@OCfsU%zrJm4dWS zGI&7%7+Hnp6Ck2QRI69GJvYcuN)zJFZ*CuAL;E)``O*q_`!m%#sNvt^aW7=ZLFB*P zXMPTc7}u8aT)=<6#Tl$~r2k5y%*x6?as^(Tq0iz7_yP;Lx@2wd--fpFH9KkMsJJ_r ze!$lXBbXB8KFugBeeQ{CiSM`lT;z9xcZULUxCj%K~qg3tNX^lmMMdgzg$69@xOXgT)gJtM{7}v@a6X ze(rUK*jYvC+v{;1e#PbjKVtswqQ9RzPz9kubT*oNNXNxqq>du;eR+tQ1;4N_)&P>O zAZ=TYI|o9Jy}XdQ=dF*bocEqzf|hu{GHW-p&>WoZ(OB@!5t+X74I|4*{OyQKQPJmY z06nsKIiHJ86+X=MY9>}`Nog@J>O23WbNzg_dU@wH-a;<^v!IN7dmGbQUo)X8)ZhvO zZFpQD-)u4kEK?p!AHpOkO;QE120;RJYjN<0Byn$oIZWlo`Z?mkp$7g>!?i}+j$lJT zi%N++MtVj9f`1};VGqzj*k><`ir|)4cMMQO@|xZrqUzYW=ix|sxWs(w{(ZMz@BRv= zfrN>V_)x8uoN-lZ|A?+-WE*Ex)&P!wquz&uTM}j}M=Yb5J{axv5chy3>9t#kWHlWH zUNtZ2Td69_@8`RhZ&}|Xea<+X%x8J1e4~m^Y~D=C+lmv*_oj$&iBflYkui&fv0Cai zCUwXq_M1z#NZv7$*gdGplUyo`3@ki|Qlv7FoSW?Ge#pUA)sJ-Sa0eg z{7iz*L#XDRAlq|)p8U~lyyVDezQ!6kqm0|cid7qN!Yj_646^(aeZ6*iS?GQbA)_JR_VgF z?jHDp+IGFWOiOyfE~=^b5lOhV_f>v#nHr4kJ}xWvUiAm?);dYkJo)lb-B_5Y&-=iJ zX={yPv%gfSC^-1tjI(tUX88Ut_a@VTd)9l)5!4nTrGN8lm10ZewQ|0HEMzvLpeYh9 z2`Q1MTkMhtnH2Iwi1^AuP`rDQl7xJUv(pa&wk!ZTZ-Y4?#S(g zU45e;SIqU8F~*#kIdY&xy-|oXla{Cs*o_scFfWk_{e`WBQY2QQIt&F7HV7Zp%$sTx zIE!qt_0NdrcWX{n8SY@RH>WgUOZ68=sUcd#90<%zpMteu4P|wJ2Px0n-vx3omMaJl z`nQ#@Xw4r}j{s8wYD=ymmJUfN%}V-*u@TdVe{`gd75;Pv{deC<$Kl{G3NH>AB!E26 zx_bPqu@iv=C7-;MS;%`1*d-C8zK5)?53txr@(GLvj6(Og$(fa?T`gfV7vIpAS3@m( z&iy#&Nu<${%kzv2ckR4bb3)#@UQ{jjlvNs6OvGZh^EtPPO6enrg6iwtB;B!D&7fw) zD&kJMT*21r;7b{eu(FHJ86un*I69dShEh$M*lVM>fn8A<1y#*0vuGxr6f#!10{cMj z==eLqQ}Mh{f%JA^3D)Cl6PKzQ0whbD-8dQ=5m$~F4VF*v>{0Jd zvZn=YqqnmQ^+Vii>lk+pxla49wv09|gB8O}I=?Q_fUZJjJtn)hyD+u6LC);pV?N$l zWv3~+hJmI(6F{}E}f-{r5U;mRX%SVtn!fgbdn$O_7d&fvK49oY`sg@>}Ru1Ud`x9w}?e& zK3_Lm84g|Ql;+EyQdJ_!!MHBDY+p7`8BXiw46R2%6R6NGjtQt4`QxYf2UO9e0+b4< z^KOA~n$j#8c+4Zf%09VOHWk{rl(0irT70Z8yD(w zea!;_0IHw>K;r)&ZX5sgDgJ+39jaC2>^B)32 zO@qVed`f@fzCZdSQJmK$5HjI`Mfo1$|Czj7aZdvbx=qC}id0m$5UZMs$^x4lkokrW z`g#)PtYS??<&^q+$YYM`xby^d!47o^#uKbQWbS#4-Ac^#F>=?ymCB8N?(DK_x9I(h zZjbU{7Ic$pUcZIePsSN^Cn42AD=6bykM3gN^deP6Q9fApNvBlSBkZ`d&Huc=C-w1y zOf>R0`f*bA)t=_?f}vQhyTWP5B9xF_M_}kucT&g^l+Y02<4b6FJ;en*FWP?X$WEilu%j3fC4UlOut6VY?7# zFX`tW_F(DLH+ET)yk2JMO)MeW)F|+{0Nb=4lX%guwY{_@JX~9S-#EJ|dXOT`lSMN) zw{3J|#et)bGWd8Br~HA&37$$!0BNJ8pO|elw5t8!itLzA?EUQg+tZj-A0$M@)J0v2otQ^V_+99n!!v>9nq{Um{>c0@HmCMJU9e zOkH?~=?yXyjeG9Reg_1@er}C4 zXdRY00?#b5D3_6AY=@Ur=O35bgpHrUk;jv3`6|bnPNgTGB919$<2a`-59;IkdrZ({ z_oHtGW=I@F5MU9;9t11;bx0Mf%Qw)*aNlatGAjlFv?i$#{LhHu4~-WUwKPVLdigA$ zy?xy;aNC(?*LTo7P^0KosgaN99AzYI?tnDuo#mev(#jfYEBi^qPgdsU*vW~vil#`N z@(b*sJo?>UpI^Jy0Wuj;Q#h_E1hXuS9T*wa{JM{r;|h|($6cN7iip&;vYD~1&foLH zfIbQY;hp&!$sC~%VyBo)VG87Ath zI?=cwA*s>1g2A#OiM~;Tg>b2tX}(p3aV0Z(a1ZXW6kyRA`KY{KOH)%D!=3LFWm{)+YP&C)e;EF#d*3fgh#S^`A@+OD!3OuJEM*=X&6I^FR)6C7v zrae46*x>{{EGK1|&l;Kn(_cYy=5C$(TRRfPN;`8>5p6CaCkB;gnKB+fj=+_JoMX(X z*}~+Uth3xHoV%IYEV?S>$R8gJ1+nC)rhn-+9BTX?HWcjz0Bdw3U0vpdIU^h5d4Chp zeZJRD>%!ipZu;jBSMO;mk0IOI`sqdF?B#XxA8ifJigMHd7y!5i0|0FP+gyA9m8qpNTPP$=D$D7m(#A{M7frKXxpY#`>95z7N%*Pd1QSRCyqKn53Wx7cJ^!P=W` z>^JlJ{7qoKTJ44>S7BGUq=%YFCJ;<5u z4cxm7o6ZiI%r*w=RUyb3ytOcsw~?ob16p4)G5CETQXB(ph-Nd40`35NuIR2xXh3J8 zmrPi`B>^Z;KEjnoaHP2c$KuU~-L^E&L*xp4p|I$njQF#f$QMAA?~hxRx$jkhn-trV z_XL|>2>gXEq^ty4*&(i76?Lh@-4hYGmod@3m{}bL;{nyo$(TPuz0bA|DLx|jnBGrR z=p~HS{ZZ4gZHNeKHcPu_ZAeS8zw-G&9s9?w9uXI9)JK}yC`7@cOcVoS3lgdK^7&(* z5rA2QAEhm!2DNIET;oATRlsY(e&53XuTHy`#1k z0FG>M=Y-efAoaFw1(8WN;udp#l7{C?tdl#CQOOr7IdGH2X1XR%W05%v}5}TIW7ImTmoCwD&ZP?djx?DZ~u?*1%hC zJD$NWPr|rNLeQH4xJ*;?&jG6s?y5Ow~bbXT)D6%b*Wf zpOxzs*ON-l9gE)Tm05Cf`}xtT2Tc}@vHMN8Td(gA?2MX}^S^v_%MymvxGx1egg>@o zFbhAsm|U-_^r}CXIT1vTgT6r@8gwyT0%z-_7smMDyZ_NX^Hb3`0zd%(9V7ri^Z$$W zbpM6;X$@`r3voo>Qw`a(cu2Ln#Yq)VNvKrFY!qa$jSA(W!eRV7{7s&f8SH`HT34o3 z2boOC<}Y9#p2ukpja3G#=7rhZ&m?feE3AA9i#FAd%a``u%iUc-6G8qjXK>i4YwagT znj~|yX<%F>Xu6?17i0_~O$HgJykaymC{w{!-{2m7suZMf2x0_Pior7|c1S-WXtXHe zDHdqbG@pMCw4^mGENHwUiV0(rp)+|Rs4^0XWMjTu2^N!#^*SOcsW2fNiMh9Yd#LOp z0xayHrV2EzE37Tk|PIeDx;I@N}vN!c_m=%94ODz>Pw`Q(=qR-)w*FIu5V57BGskq)U_ zplKa)tRjhKD1cT%{?lc%9ss;b6e@G7)BzBhE%y|&R#Dy6Q(A44z1@h8YF zFuhF^Pvf-^x<6{LaS@8Mq$G?PG@GHQUt{ZS>G{)E=y8Dqu@DACAQDNN`^luNPDa@4 zaU;Sr^4Qsxl`inJ#8{$;ZQ3NZnK`O+v$yktm|-Oolqb zQc9HIGb|7uve?30_=V!lOkjz^I2eeETC0Y^H%7!v!R4rgsIVLQ*JDn-EkK@P1bpdW z5Z0HXd^XF!JD^*eOt#^6iDX>F zkwVldL>-yKu8M|xj5}viH{;uH*uhMY#!a}#;QI^JNOo)%>(Ob6*Q)^z*bXJR=}aT{Mr z1Ks}!^-PDbq#%`=0+sV#gB8mx`a|Axi&CME9U~1kA?82J>^ZTPI$3B{ zQL6rLxU=Wupy!|7#gE=3LDLWcqbKXi)jHA$1``rFFt_dI{TYvp4vNkPrp z%FhqRW{3)X!Ah#x`F}Kcdk{;lAano##_Ydmmg9d6(3+arJDUENweG)uTjsNN!r@5R z{iZ?8X_eGd5_ylms*x)fj!GJgq$?+-epr5&1~(T(o`+=wVk}70_gi+KiefoZOi0vP zRY!L67cZQ*cQ%F)z{Tx)yS@E99X7v!p?}x$Fh4AJL)-Eo7|Vij94HKDQ8270liN632^ zA)H&wPs$35Xa=Np;Pn7p?B^|1M+`Jz5&GRpD$f6J6<-+T#EB@pHm-NbMu=3IxAiPC zlqm8JPe}DRZ=Ui*Sy}VXUsOk2Li#ybhMtoM!TP;k6n!aH3|?U?skqrf$i_XwqUb(@ zH(O?GF9yA=J9oM>qjJU3TjD$v?qevJx`HeZ3(QbSzZ6MCsUbe7u3KLl2#8Sza11jP ziwO@(KN4Z(nOLpb9nUbjC&Un0{N@zt;oA?Na|Ws4VvOR_>&G&JI4|Ufv~XWiHrtO{ z1ccqDSp6>2fl4hlZ+ZMPe%LJh+{6pk>kku6i!Q2z6ZYKFdNZB3XhM z|3p2M<93qHgBLddVm=wtkVu_GFgVKhe&L=}v^*yaW!!h!vhR93IPJ{HV&$Hl=m{rO z@>R<`f<#%hsPG0|S`M&aik3vRTuN&rMWpD$;#jL<@A?%f`)0b85EmLJF%A605)b+JUpwF%{f+cwo(;(Ka1^ovt+{ zS%lfX=fKI<`H4D7Ij3}SLGDEB&Ld;&U~}?jUo3|f(_kNVP@*X0K+-a+ozKu4wA6%m zpn4Gy+Xq^EA`s8jlPyE3p;FeAFm-BP znUGAWC*~UBKI0M1A2xV+dF0|}9;8U*2w_i&IMW{~!L$M=>EM>T4;%CHcnr~zA&6;# z#V_!*l%y{bP>`k8bZK%=UZjqCg=ghYT29%o&ta!oW#CW=s6t`fXW$YC>0 zL6+-Uz^Tf71NS850u12YSuBiRs;pav+EjRl=q~c8)7MXEnK>OxKb%HfRUh2VhfnfF zzuWim;~L!u#NnD=BWapIxm=qLseUC8Zu#G3KwS7;;aLAY%hD@E>h{;s^PA z&Ja%prVm%T@#*1n4hnXvY?U#Nq-e1(ARkY&egeRXgobC)^3$vt=5Cn1o}1oAic*-hn_nfKT>R5|Ktd z0N)pFYrGZ*`>Hbgr`eEI9btUY+CHGPcU5Rx-<9vS@|2GTJ6SmgnDL;-9K(dKwlYi6 z@oy=KfEznoXJsP=hpTV(PrWD>%N^DBnGCUqyMk5yqPZJgrA^5JC1IWx5wMG%|GtQE zUReGSpXgc^48a$56?L8kNY8+EMBmncJy3>|lCmJFOX#uFS^^wvi*{ln+5K&ixV85W z)bwK6`FMD=M;lVabsr&NJz)$eluX zr(3z;5*362T+#vMKop`>UMLV&5(~02<=}L0R#JMS^KW{tpu3e&X+Av0%xE3-Fz#?9 zQ=PWvxIII6##5sZu{t_opl3hA2x%jXvaa|0YXvThb{DAnS`=E-X zy?YUQ8OU(hkMl6z5)x{c1{8VLcu-72G6@jX6%Gv6}RvbeF>iP-eg71Iq_{%Q_%EVNqFD#}x12B)FH`yTGv|y%F2$ z9i8|u`}?)bfnbR1_*@o; z1a)MXozsfUoDP{OL`tbUwcgUQW95wDKZb$YMd1yZnc6QFLK!GXFeKm0uZ}{Oh~nG~ z9jV4*1PpeJ4I*O@ttME6Q&gCE2<`eB9y6!twrrPx8YB*I#bbV(Ff698u9wd= zT9rEh4fw-KmsWIW5riI_-c1|>(6>BdduXOneC9q8!kHQ^k%!<3-9Qk09dl%`id_DG z^fQ^?7^=hS^N_#Bv6N`qKvSjt|82|A5If@qAzK^dW5QUf#^Z(7iLR6jdYgF@bAo`Z zZ`BdmEChc1ll>}|A;Pp^zV{IZwxl(jVVP837=a1p!JA0h6FLwX$ik}qjd?Xbv0@`Z zdm{@;N?)O0b|d0YrSmB8y=GRwF&@Y(#6kR7! z&~^N6^ti;k@nScZ5(RxeCW-gC#N)?b@5ZupgtwEY4PUZjI}@xV#gFm{SX!3U83j^5 zDtmS+eemM&t8KeK1^#rCpu0x4$K!~HcWA?p=Q1VbvYtL&chap!b7&fnJ+{aEo08~q+76zl$dhbahWO`+ zl{Vb*$&?)a35;=qvcf(8X@a;p2!mc~==PcENK&74X0Ar``Uu#Rb7FQ%y>hQ*BT?Sk zEbG0hHVw#|9(1S@`gqA8Gy!VdO!RXb2-GEE2va!7A%;8Q$mg@`opv;(BZHzf*AUTU zXMC+eNDO)^)KV@?poF{hNEV=Kdc4pfqc$TN^rXn$6ra@l0^g{!Ne(~Cz@wJdZ7$mV zeV+WjYW-d7m*JPO`(x?*nfiWODbx%7)KUlEw(Ryakl(V;t|RJ*H|=i0Y~MFO;x`## z1kn=*fjdkTF!y_{Q0vJ}!ibj>6PjAM+8Xw8OL3ZdxIa8xxhXPA%+43sC&h2o?$9JE zSr{nKnF!suu@GSkUl>r=+6oyIu+7jm=cXJ(VQRQv!WbxDp5`$TBC=`V)n$rxn=nN5 zB2{|b>{YgX=xsCS*)%TTT4jsKONJ^iARq680hIiZTA3CIaKbtJDn1`I8E5tW=Uk{J%W55*u z)0*hiO$iVIx2XUcYlpICnf&KZt84teRsMDJ-Hi~LRGW`4YmO)N=k>wLnA4zKn=Ws1 zYUbYBG8ome&duGM|2Iz?W!xU-;X@mR&N_ePg#}-b)pn0c`X{q!;{#avNITT+i-S=m7Yn* za~mZ+??e%=y|>6TNK8S{O~4DF?cUrXo+3v}LQA2nr33#Ky_<%+0}X*uMAU z>(R-32kxG#)!*Vhl>#(1na!UQ-zzk0;VXRw<$=pyyGBT@-aw%sz3y8KKi8M#=aP8K z*^Ath#0BYQJ1YokXYGwiwH9LL+-7MB)3;PbPENbuLgPEdOS{iKP7K8(*KH@d;MZ$A zH|*kk>8AkyM2p=^M@5HY~RluN$)DateDg)0P8y=Jw5wWI7)De3fGy zA7W*6+`_FKud)@H1$brnRTbUnJjprKbGpNvv5kL-54WwUaOBhwYO|AHom2C(+2Wl% zoOT>=X-=J;)6pv<;{B~#o{P$cA{nHlkEJ_H&%Y8Z#O_>N4n+NKL@p@^Kbhg{oNvaX(UlP0TA#za5Z} z8(}h*P#lU7m~Z%th?ME*9cZ0eT+oE5fbbofU^xNW0DAGpXb|y6!IK!bw10w{{>pQT zBo#8(gwjk((I=T$8j}OwX~{6rxQ_HUlx^slHmc{wOYEPWDr;0j&B)n{s2=Jcqc>vR zjZXkK1C1+Q_o_?9P_321SvghPznQo9Q08}AOFxDJRsxR?(+X7q$JP#I5Thj7P|e$;gcTu{_VXRp#IPTCxmJ^kHw z;`ztK9Gj>VlTKz{`k6Z@z4l2vKD(qxe!rA5RvVHkRsBin)HVwh2sJWyHqU@KN(~fX zI;{}HVbOhd%o;~A0`E%xNsa7ZIwAg__D0)!`lkpqiX~Ge4$LLz6mTIe0hEguv@$W_ zdcyiW{`iEbB|2r?Wc0^5ERCaVj})k%QPglG9fn}*RIx5Z#WP{~_PLDGYiRW3yr__K zU%99{BC)bO1@RMnBq4nVRJ|gJ%MDLH;~m}nb`Ob(EM~c)NR+v-f__;=s zd}Cm7ytJ8#ODX>MWg-{^-CdnCBtDCU-kdxRU4)4039ynx^kP@z0nu{k?9RRO6SipA`#%V${D| zhfuZiJ|rofup#K|vz*)6)IHWMEVBH`qV^~xXm8ZMO( z#?%Q{ggrx(ZA>-Ge;cjxyQ6U2lqwK&Z%I#UrvET(zXV3>UKYV@-kRslxtvE;ZZF9( z?uekmf4C(1d=&^Jp=TdO%jQ#+QnAlU|HP=*PshAu(rH=APzO=9ny<#vxJ|FXZ`B(N zK2PXAcHU7xO|Qby7o==Eu@_IYV9f^HvA^-a{QY}@^v*fLf~^eZoA(#)5$vfQV%Em; z+k};!u3M&uIY*_+rS_nFHk7eaWyT>H8lfO#W}x}8XAdW+MSK-T9aeiQD3=1O!Lmr1 z2J17aR^bRsDTGIK0zJ1cSAB7D_PFJ~I%v|o(+$2sDW7A?dwsmbcLae|x}i#BqqYZa zHQOifqZ^-oknQO0Ag&h5*`~bs=4tVqe=cnUr3d@llW>_^)tysMh8D0LE7Vv@0@Czid*P+Br%m z-i^KA%*<;p4C);o!S>(Y(yZgu9Zc$rD?rf32rOaD_*)BamGAZLEw(23uS!FQCm6z+ zSvBqEUwePK;X-sAWRA6>{=YT$dL+ze8n6}gF&l&%T*2{kGbuTIw9L}{rXQ1E?NDfKzHV&DybI0*wE%kV**1Cm9_(H?9 zJU}!rn0C}n0r|?-acC)8L6pH5n7}tz7Xx~Ff?Kou=lMevWz~?OJ#(%Cue8FVroqX* zSLYh)e?K78gm7um+-iBwT+B@(ZmG&@*r1&q&RoH|1Ov0ug5B>W@j2xN>aHs~qUG)B z`HCZ2QMd{o+~m_6F3}i}o9d2BoLxU>dzNadvQ6CU(QhJ|Fo^VbPQCbHwae$w2hsqe>+Q}IH{`#HU~lT zJ)BD^U;^KdXB&8SfhSBa366!d<-tgj2V0{C1s5qGrKRS!Y2x@5gU)}j{u#B4Wtif? z8CNn|Dc^AYE4hAKxdbP`xgdzzUY}WWSV4RQthbR$z-wc}IMvRry;+)bj3>Rv=efIXtoys1!rk-FZ7_t%m_u8NYQ||A zb=(IGXMJ5Mx1Gg6oL7#85b(?X=KEk{?j?$YrDMlk!9^?zf4i0u5(xae9AD(PJS*k4 z%k_=`)>GwvX0u-*bN@aBZTT-P4{Ah!}eNj@DR)EK^L{)LjjX9I}lD z!M`L{?a4Y5+_si8MsRhqs{s@wJR#j(_3t)1*X?U#=H`I&k8QoTE)ylQLNJPOG3|foH7i=^#y?f| zb}q0m<-9Tu8FiBcaygdjRcvF9S&Ouv)=a`aq`QKkbK8}AsAg#J#s2)q zmR;=2-HRv?06-oF0Py?ojH6U^F|x6AvQTuiH?y=c{l9>ywrO`DiRkmHp;{I1KW|{p zpnybZ8its!MuD)siMhLfq?XDE&>&{1)j)scOn)4Vw|n)}Ix zz`SH)F|%`KcOh3$%puFX@RbH3o$;o$s6+jvc?Q<-H>-4$p>Y4~2Ru(8blx2r;qb@d z4vsSgyo%m@T8KCWGU2sgoRq}4NX!gaj2L(U5p~acg3To3 zXE+XSijAy~Nmx>fc%iJ?Bywf_2j}IMcl2F$U29ToY7SQ0;1UdEVo|cLXemg$IFAu$ z&O))4@xj;gbK^Q^(PN7gNQKz6r+__Sq2_b)cBdJpHbPo%H730gXP3h;I)YKJ;o4I& zf?yvS&9v-$2(|g%ue;}G`kD*C?i!l0`kL`D^#gRK)4Vx1+CGN1*)U>6wkW1#ZL4ig zlVUqEy3%^~uYOwCeE>lIEkH!fY8fO%DMFaWG#;sl6hMSvqYk#Pk7`GbQiy+v%kzVE zcycTdw!)9yE~$hdDOR@6QXJo8d)X0#Fuh63)6|OXupA;jc%9Ac`n9nqAegt(y6k@w zD!UF7aS}%9O>`i$=jO|EhA{1DKKbWTrZcK&wfgk+On^XZ`Vr@Wr!Uc#AOiFsX0J-03=LmuoSDOX9>tsa(2r}#8SyjJz$c;l@+GQpAb zpKz^*WECG^)5uNInbxZ#8HUNARZ%>a+B%sR1x(!a>kIq@fpe$6?;AC2_UQ~!ZT29R zkyt(9J1GDFQ8z);3D!gm8O)DUc{r@Eq+GUsZt(VantQQCAaIsIBOg z)=)qjPZQtw&p;!{-pMG?Jb9X21N$3JIA1pPs7-!Vsh6)HAU(P zgYu`W-~X5!pUXFp#XcoHKluFr|nL3)<8UJ5(;+ogeX_GVMe`vXCo}{)W z8}iJZ+V#~|tv02)oBR{%3E3GL6M{#H!AwwfK#GwctM+^VUeWu8#HSb87y;Q5slEMe z41AXdBR{`efu7sl9k$0}MWdST%&2mXVn?74cp^HzwAqrMtkFS~`yrC!un!U0TgIEr1V&ysdWgs>Ev%F%K>(w! z|44x*0i=gq<9F2QZ~)*s?A)o?{0(DJms z((c069m9X#5yCa{UNY5(ZV(U?`of~g7^cdvV$O(t(85STyx~dv)BTr&gB;!WJNQHt zWx-<;IQao*WXoVREMlMt9KnADWPu^aJfX6>aFdoS!eP)XNbJeRuq6p0F)|$d0c3{^ z{eDezsf7K5iBRd`l*n0}+a$=oa%s+zSGbO7fK{6rLHKDd9vEEXP;nZh%=J)8Ikrcr zDvLBsrVJ-@MvN2OwY1?fF=dgxcpAGW5r+d6ut?Z1QRzMIEkt-N%Lx$bAf%r#2GTiB z{CxPobAw0;({p)<4oNgwTF{m*DT|P(f9lX28jO;XAO*$e8Bwj7CAvY&{R@kUnu(qX zBGM8GBScPclpznxJ#j3lMwexXBIab`QbOZ}h?NMXk|^a=kw^DO4ltE+<`}n7U9~z^ zDamBuIv_}PNn3QN0jvjX*ab#+>s$WQN8|44!feEQbUR!i;@z=A7t>x;3GP|x&r%+% zX+gf9r-y-{v0a0SLD{BMTS3Tst_O-5FrW>xtKuf6%M+4BDFbUE3S8*{BpE`2doYn9 z`8kSWC&0yJ%G}ma)p@8)f)X9}lKh^{W?#MkVtZ|Tt&7@jg0DwSh~4`XUF5yKbxCd# z7gZK&Me}y81-UtAPgWj2Rb|rX2M4+TX+0!1SR!X?0Xx5u{-^Cd$t4OYsNI4 z@1`})ij(Gg4AW+0?|>BdML(Etle?V6(nl0f!q%+= z(jw$9QQr63k{=sSUKiAf=DY63{W2L4(XBAatu(AHL(%1k$0#k@y^RmLUh|SD4$O8y z)ZS-#x=Pugs4V81X{!LmS8CUX|G9AN>+SY>#dp4*fNl!? z-mhEsSo(-bN!;$3FqSxS86F4Atb(Rh%XvL=VvAOgWe_V6h`rRjk|T@4XHE8;)AwQZ zwg6EJQ%||C!Q@LBF}NP#V~VOAar&xscNI zHR46IUcQY-x5-3%y{2zT-_g=h@#&L}g2c+x#;}|bDIi_4+3k>iieZ_v2FR0Npd7xc zJZyX#nPmUG;@r-OMv11gW=Lu>iSQ~EU`a+JzpW`r;LrR*C>ZnhZ7thL3AOhb@p2O# z^`EHnmlTsMyuyF3v#W`ko7|myNvPfK8U%kpc}Kog2UN-Nvbs}e6PJcS#;covfOoMP zQPn|aG~&6!b0wyxo?$B_?oi zsmFLN7bj{6bIlLcjW4?mZj|-me?IDzu!(28zc)MGM&c1JS`oUvC>DuwJeP2+(|JR# z(4}k>x8RqN{z+6-+*=Eau$A$6iN?Zr5AHj>CGVHUaa`Y(;&@e1T8i!L?T{RvH0Bnq zPDe$Ri#-Rr`bZTrwIeYX5PiZ~(;r?8XI1GM%o9g3rI&uPTPmeEL~s9E&p2W`O_rNP zfk_H;0a4YzO&DQtY#X3FE9v`GGmuY2)Lzm`tIM|hptS9-+3^gUoyhD| z*WtP3zpLU|u9lQ>I-d>6F2=*4Y+#jaqO(>B$=p02=1IyvXDSF*XEhPZxH23$?5If# zOuwg?57~ zZ)cMNEzs?5%JitGW@TrI0mJCtyJdyvos|QB**zS69=fKd-lTbf-+B8@?v%%9>@c~I zyl2oOShIIo@QYTs8#1C8n;ON@C=w!y+*9i1WmU@A;p`JjJ_2dp*o2njr;CNF89%}95?rMM25UWYs zYS|dl)KdxmMR#*z@`1dXAl}2^LBEs(T}}GSc1G|THb_kQ8iq=-05N$Eb*y8ts9fQK zVb1Fin+Lo_29E1Pjv*CnrVibKF;U92lr&&DT9MOJOJ3WAhl7iN$fO^<8-n>{{-V1h z>g{e)AH!z3s3>VZRAeypf(~1W%(#NeWO~3cz@ZU$=H37tE{l{`uY9^!A&9NPUOJ^( zbfydIei$*8tO$KYq5=gs9wZM;J~f>IDbfWbd?|-Sx#ije@f}Btp2rHB+prqjRl5FY z7kD?yd=+sV43bEv{G75AM}6F^8Fm)^XFybvGwQI)*GpNi2W42|Xf|y#fn`;nhXb3% z6lrZ>h}c30u|2uy_i%56+K}4#NO(t+r2Jle$0dn*GEDVqR*u$IS_y=e=P4*qmT0kL zHdbR@>L;qT&z%}&f>hM@@~Pr*ApC=WnRSbD+mi1%0F~C_&t-Z^jc_UZ*=&&}P2BhX z+>XhsK73Y-PFfR}X;fpBDxNNxZM*g)A4Ujut>C5iYper{r7cy)VDrlv9q;DWH_En5 z_i8Pgg4fiCt8=ceOox(8L*h)ja5iKQ7|R6HEr@3{mD)eh-_ZlN(D@VM$Y(QXBGJ6# z5(#AquW{i%;|Au76C*2BjMqwMwUKv6lY01$qX+sB+8kC1f_4-!>;#$8I}PlD1dYzA zy#r??__`|-EXxK_{sqQ>8mXb?y(^?^=1H;(gf0cnk@rCLQ}gRCYlv6C{uY@!BC4P* zpFPB*kYSw`bTJI$L)bOI4h4_Gvm+uOKl9_cF2o6FMzzjaN*yRho|7&uvxHb%l)JF*6k?ac&ewU-3jK1G(QQl@d+hXkOz7t<*IP$PwcGxZ zF?r}zau8?7&#%J+G4E*q3_}@W`a@WG`OtuG%{KxEUy^8y!*%0hT#%_RZ>CllF#UEA zNrNtkWKd5krO_&j+)l>;fu|@bgl^HR+=@`{4&moE?ca;gY9C;Gt#xwBBD3P^IYKw! zrkZsB-ofo!+H$7YmcM}Cs^YvQDCa2~meCrxW4>IAhW*8ZbCvn&O30)tU*ytvxb0oI3O&;`yml1&_Ac>Go^zL0vvG2`x0S*# zXWj21m&udLin1DSHG}o`g7fM6HMHtJ4QxrRug;v?fTgPEW6U}Umjl)>=>xCi8Efsc zIsMpUFrH46{~oUooN?DFvT^ z#K@<%!-3+i?(bU5=R7D}55h6!ssl%qzoZU-wSR@kynr=T$mexAJ3vFau}r^Epirya z*j%*-u#gS5Ck{XqGk4Q;7W2gYAfk*kWN_VWb^JrUl{5`@at`D@HV`bD(Lc@vFu*?-GD77u_j#1z# zYPF;EnNfNDJBR>M>FK$6l|)_p&@t<*=5uv-m>0mcnP9EcA;51g&2n?u@8HLa1hpE} zbbCoXC2zj0WoN(j{*^MTNNwlKZ33Xz;qZK@g?)BCQ`&yO6~+*Fw_jb|qOCH~ZG9hk zIJgRmnfb_i{mvkAyH~qNzbTa%xsH-3`W+;?J{&!P!}O+LU4%c6*_#6k=-8n=lSlDI zGrHE(cK!=GO{L3*;o8tH%pFK(4x(}qw%({e6mI)sl2Od5tMMC!3F7qkH$=St+ja6M z5ZpJPdi0N$;7RY4VCJl;^r;g|t+(BQLM-mhqa#V4BxNbzZoS3l7YSK3 zjLM8vZSCZHWdX51ada31u4@mbt9{_c<0kon^S*X)_V+{YixpBJB)3yhL#ZsI?CR4-;D?!zKqljwW!C8x_80~nGnT&ZpqGO+ zbF@A#bvxYp{p@>EuP#1)eRkr>?iu2w4(X-6)>UVpE;l{K+%0JB*^Pj4YgfWyFAt=* zJurO@>)YeQ=JV}VN5#Q-3Y;GY923w??Qa?ERjv-42-YA;SSN{w?%UHz+a~Eh4yxQT zgst1xX)|zPMXbTO-lElRUAS!JrHi(cA=|bD^Y7aqUeEO~hV;0T zs$8wi+}Kfac_w4CB(+z(N}H@b+8u;(tIhnJ-z3 zNI??;WW(67S_X0Mg8?zefZQNaJC!9w8m`kQVIH3V)s6XSutuk17R`Kd2?{NqVhr$S zNHgbQ8nd%rDBXhusGK@w^poFSu}qw?bfz?19`va+;zLKI8x4A;4pXed1!j6CqMY6> zkAybS?$Ao^e!pJ2kZ3jZzvt~mwD(73d#O5iX{!qSfjsoa!DxZ1G@4A7Qr;>@?F;Cj z%n`}oTqNvu2kc-E_!1mc;bo^{ISEuTebAN6W*$1!o46=2EPJH8*!KaS4#{AqFtEJJ6cVl;~Th>`R}2rdQzAOTF|z2aGHI zX1&RdAlO+zDr0X2Av@F2{^6@%;QL^W!%kP+f3A`rID!RL7{S zY2;J(+3<$>@Ch{N$8Q1sJMa48dwN(RdmYrNeWG3_&<1i#~@9+G+i`p+qP}nwlm+ftxB7f zwr$&$uC#4imG+5mdgkoiz0br%&zau;pLqUUSl3!{LuGMP09B<}jyj5Glv)e~+G{dm z2snNHR}gKlLh$@-l}4ZQumYjY#CJ zT?L2n&xf)14CFTy)L~v(8L?rnQ@iMl3WdYtV;wlE`K$@@vcw<$xl>%qIDB;AB2qyjK95 zz^}xdhD7TM*7d-3XEuai$QGEX4GA>?vVa7jB|L+U)4B)m!)6@MuwA@qRF-M&?^hzK zJe7AFhWd5>%aDRt)gSYp>yI4t52`B^Ds6WrMkpk)9#40#D;qwnzG@#I%+#*7qj^8c zv&fXKO1|7!uGbGuYwx=c=Ve>tYyfj%4ntU0~Zb%;nWLMYqa@rEBcCut3pfL!{RG2ZDgf>r7u2ZPho0f%Er;W}9& z2-jx^?truJ;H3!$u0SOXK^jb!q%_00JFgo)X3 z9Eud5Z>E=y@(3(v?kWXCOvu(|d96~apW$`k=EsZpR!brmF%h@ZMmy&3dl/sP=v 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 SCT>?1Y{v{3ve~-hV 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

FVrbp>5Vl_2@^}0Wu=Q4I<79f2Op%#D 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�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 ObservationIds, + AdvisoryLinksetNormalized? Normalized, + AdvisoryLinksetProvenance? Provenance, + DateTimeOffset CreatedAt, + string? BuiltByJobId); + +public sealed record AdvisoryLinksetNormalized( + IReadOnlyList? Purls, + IReadOnlyList? Versions, + IReadOnlyList>? Ranges, + IReadOnlyList>? Severities) +{ + public List? RangesToBson() + => Ranges is null ? null : Ranges.Select(BsonDocumentHelper.FromDictionary).ToList(); + + public List? SeveritiesToBson() + => Severities is null ? null : Severities.Select(BsonDocumentHelper.FromDictionary).ToList(); +} + +public sealed record AdvisoryLinksetProvenance( + IReadOnlyList? ObservationHashes, + string? ToolVersion, + string? PolicyHash); + +internal static class BsonDocumentHelper +{ + public static BsonDocument FromDictionary(Dictionary dictionary) + { + ArgumentNullException.ThrowIfNull(dictionary); + var doc = new BsonDocument(); + foreach (var kvp in dictionary) + { + doc[kvp.Key] = kvp.Value is null ? BsonNull.Value : BsonValue.Create(kvp.Value); + } + + return doc; + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetBackfillService.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetBackfillService.cs new file mode 100644 index 000000000..53a8647c0 --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetBackfillService.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using StellaOps.Concelier.Core.Observations; + +namespace StellaOps.Concelier.Core.Linksets; + +internal sealed class AdvisoryLinksetBackfillService : IAdvisoryLinksetBackfillService +{ + private readonly IAdvisoryObservationLookup _observations; + private readonly IAdvisoryLinksetSink _linksetSink; + private readonly TimeProvider _timeProvider; + + public AdvisoryLinksetBackfillService( + IAdvisoryObservationLookup observations, + IAdvisoryLinksetSink linksetSink, + TimeProvider timeProvider) + { + _observations = observations ?? throw new ArgumentNullException(nameof(observations)); + _linksetSink = linksetSink ?? throw new ArgumentNullException(nameof(linksetSink)); + _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider)); + } + + public async Task BackfillTenantAsync(string tenant, CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrWhiteSpace(tenant); + cancellationToken.ThrowIfCancellationRequested(); + + var observations = await _observations.ListByTenantAsync(tenant, cancellationToken).ConfigureAwait(false); + if (observations.Count == 0) + { + return 0; + } + + var groups = observations.GroupBy( + o => (o.Source.Vendor, o.Upstream.UpstreamId), + new VendorUpstreamComparer()); + var count = 0; + var now = _timeProvider.GetUtcNow(); + + foreach (var group in groups) + { + cancellationToken.ThrowIfCancellationRequested(); + + var observationIds = group.Select(o => o.ObservationId).Distinct(StringComparer.Ordinal).ToImmutableArray(); + var createdAt = group.Max(o => o.CreatedAt); + var normalized = AdvisoryLinksetNormalization.FromPurls(group.SelectMany(o => o.Linkset.Purls)); + + var linkset = new AdvisoryLinkset( + tenant, + group.Key.Vendor, + group.Key.UpstreamId, + observationIds, + normalized, + null, + createdAt, + null); + + await _linksetSink.UpsertAsync(linkset, cancellationToken).ConfigureAwait(false); + count++; + } + + return count; + } +} + +internal sealed class VendorUpstreamComparer : IEqualityComparer<(string Vendor, string UpstreamId)> +{ + public bool Equals((string Vendor, string UpstreamId) x, (string Vendor, string UpstreamId) y) + => StringComparer.Ordinal.Equals(x.Vendor, y.Vendor) + && StringComparer.Ordinal.Equals(x.UpstreamId, y.UpstreamId); + + public int GetHashCode((string Vendor, string UpstreamId) obj) + { + var hash = new HashCode(); + hash.Add(obj.Vendor, StringComparer.Ordinal); + hash.Add(obj.UpstreamId, StringComparer.Ordinal); + return hash.ToHashCode(); + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetCursor.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetCursor.cs new file mode 100644 index 000000000..108d399ef --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetCursor.cs @@ -0,0 +1,5 @@ +using System; + +namespace StellaOps.Concelier.Core.Linksets; + +public sealed record AdvisoryLinksetCursor(DateTimeOffset CreatedAt, string AdvisoryId); diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetNormalization.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetNormalization.cs new file mode 100644 index 000000000..a5b896628 --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetNormalization.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using StellaOps.Concelier.RawModels; +using StellaOps.Concelier.Models; + +namespace StellaOps.Concelier.Core.Linksets; + +internal static class AdvisoryLinksetNormalization +{ + public static AdvisoryLinksetNormalized? FromRawLinkset(RawLinkset linkset) + { + ArgumentNullException.ThrowIfNull(linkset); + return Build(linkset.PackageUrls); + } + + public static AdvisoryLinksetNormalized? FromPurls(IEnumerable? purls) + { + if (purls is null) + { + return null; + } + + return Build(purls); + } + + private static AdvisoryLinksetNormalized? Build(IEnumerable purlValues) + { + var normalizedPurls = NormalizePurls(purlValues); + var versions = ExtractVersions(normalizedPurls); + + if (normalizedPurls.Count == 0 && versions.Count == 0) + { + return null; + } + + return new AdvisoryLinksetNormalized(normalizedPurls, versions, null, null); + } + + private static List NormalizePurls(IEnumerable purls) + { + var distinct = new SortedSet(StringComparer.Ordinal); + foreach (var purl in purls) + { + var normalized = Validation.TrimToNull(purl); + if (normalized is null) + { + continue; + } + + distinct.Add(normalized); + } + + return distinct.ToList(); + } + + private static List ExtractVersions(IReadOnlyCollection purls) + { + var versions = new SortedSet(StringComparer.Ordinal); + + foreach (var purl in purls) + { + var atIndex = purl.LastIndexOf('@'); + if (atIndex < 0 || atIndex >= purl.Length - 1) + { + continue; + } + + var version = purl[(atIndex + 1)..]; + if (!string.IsNullOrWhiteSpace(version)) + { + versions.Add(version); + } + } + + return versions.ToList(); + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetQueryOptions.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetQueryOptions.cs new file mode 100644 index 000000000..63ce2167b --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetQueryOptions.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace StellaOps.Concelier.Core.Linksets; + +public sealed record AdvisoryLinksetQueryOptions( + string Tenant, + IEnumerable? AdvisoryIds = null, + IEnumerable? Sources = null, + int? Limit = null, + string? Cursor = null); diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetQueryService.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetQueryService.cs new file mode 100644 index 000000000..478a562fa --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/AdvisoryLinksetQueryService.cs @@ -0,0 +1,111 @@ +using System.Collections.Immutable; + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace StellaOps.Concelier.Core.Linksets; + +public interface IAdvisoryLinksetQueryService +{ + Task QueryAsync(AdvisoryLinksetQueryOptions options, CancellationToken cancellationToken); +} + +public sealed record AdvisoryLinksetQueryResult(ImmutableArray Linksets, string? NextCursor, bool HasMore); +public sealed record AdvisoryLinksetPage(ImmutableArray Linksets, string? NextCursor, bool HasMore); + +public sealed class AdvisoryLinksetQueryService : IAdvisoryLinksetQueryService +{ + private const int DefaultLimit = 100; + private const int MaxLimit = 500; + private readonly IAdvisoryLinksetLookup _store; + + public AdvisoryLinksetQueryService(IAdvisoryLinksetLookup store) + { + _store = store ?? throw new ArgumentNullException(nameof(store)); + } + + public async Task QueryAsync(AdvisoryLinksetQueryOptions options, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(options); + cancellationToken.ThrowIfCancellationRequested(); + + var tenant = string.IsNullOrWhiteSpace(options.Tenant) + ? throw new ArgumentNullException(nameof(options.Tenant)) + : options.Tenant.ToLowerInvariant(); + var limit = NormalizeLimit(options.Limit); + var cursor = DecodeCursor(options.Cursor); + + var linksets = await _store + .FindByTenantAsync(tenant, options.AdvisoryIds, options.Sources, cursor, limit + 1, cancellationToken) + .ConfigureAwait(false); + + var ordered = linksets + .OrderByDescending(ls => ls.CreatedAt) + .ThenBy(ls => ls.AdvisoryId, StringComparer.Ordinal) + .ToImmutableArray(); + + var hasMore = ordered.Length > limit; + var page = hasMore ? ordered.Take(limit).ToImmutableArray() : ordered; + var nextCursor = hasMore ? EncodeCursor(page[^1]) : null; + + return new AdvisoryLinksetQueryResult(page, nextCursor, hasMore); + } + + private static int NormalizeLimit(int? requested) + { + if (!requested.HasValue || requested <= 0) + { + return DefaultLimit; + } + + return requested.Value > MaxLimit ? MaxLimit : requested.Value; + } + private static AdvisoryLinksetCursor? DecodeCursor(string? cursor) + { + if (string.IsNullOrWhiteSpace(cursor)) + { + return null; + } + + try + { + var buffer = Convert.FromBase64String(cursor.Trim()); + var payload = System.Text.Encoding.UTF8.GetString(buffer); + var separator = payload.IndexOf(':'); + if (separator <= 0 || separator >= payload.Length - 1) + { + throw new FormatException("Cursor format invalid."); + } + + var ticksText = payload[..separator]; + if (!long.TryParse(ticksText, out var ticks)) + { + throw new FormatException("Cursor timestamp invalid."); + } + + var advisoryId = payload[(separator + 1)..]; + if (string.IsNullOrWhiteSpace(advisoryId)) + { + throw new FormatException("Cursor advisoryId missing."); + } + + return new AdvisoryLinksetCursor(new DateTimeOffset(new DateTime(ticks, DateTimeKind.Utc)), advisoryId); + } + catch (FormatException) + { + throw; + } + catch (Exception ex) + { + throw new FormatException("Cursor is malformed.", ex); + } + } + + private static string? EncodeCursor(AdvisoryLinkset linkset) + { + var payload = $"{linkset.CreatedAt.UtcTicks}:{linkset.AdvisoryId}"; + return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(payload)); + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetBackfillService.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetBackfillService.cs new file mode 100644 index 000000000..ba39dc74a --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetBackfillService.cs @@ -0,0 +1,9 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace StellaOps.Concelier.Core.Linksets; + +public interface IAdvisoryLinksetBackfillService +{ + Task BackfillTenantAsync(string tenant, CancellationToken cancellationToken); +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetSink.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetSink.cs new file mode 100644 index 000000000..81cb5ae46 --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetSink.cs @@ -0,0 +1,9 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace StellaOps.Concelier.Core.Linksets; + +public interface IAdvisoryLinksetSink +{ + Task UpsertAsync(AdvisoryLinkset linkset, CancellationToken cancellationToken); +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetStore.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetStore.cs new file mode 100644 index 000000000..a239d174e --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/IAdvisoryLinksetStore.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace StellaOps.Concelier.Core.Linksets; + +public interface IAdvisoryLinksetStore : IAdvisoryLinksetSink, IAdvisoryLinksetLookup +{ +} + +public interface IAdvisoryLinksetLookup +{ + Task> FindByTenantAsync( + string tenantId, + IEnumerable? advisoryIds, + IEnumerable? sources, + AdvisoryLinksetCursor? cursor, + int limit, + CancellationToken cancellationToken); +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/ObservationPipelineServiceCollectionExtensions.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/ObservationPipelineServiceCollectionExtensions.cs new file mode 100644 index 000000000..53b3d730e --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Linksets/ObservationPipelineServiceCollectionExtensions.cs @@ -0,0 +1,45 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using StellaOps.Concelier.Core.Observations; +using StellaOps.Concelier.Core.Linksets; + +namespace StellaOps.Concelier.Core.Linksets; + +public static class ObservationPipelineServiceCollectionExtensions +{ + public static IServiceCollection AddConcelierObservationPipeline(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + + return services; + } + + private sealed class NullObservationSink : IAdvisoryObservationSink + { + public Task UpsertAsync(Models.Observations.AdvisoryObservation observation, CancellationToken cancellationToken) + => Task.CompletedTask; + } + + private sealed class NullLinksetSink : IAdvisoryLinksetSink + { + public Task UpsertAsync(AdvisoryLinkset linkset, CancellationToken cancellationToken) + => Task.CompletedTask; + } + + private sealed class NullLinksetLookup : IAdvisoryLinksetLookup + { + public Task> FindByTenantAsync( + string tenantId, + IEnumerable? advisoryIds, + IEnumerable? sources, + AdvisoryLinksetCursor? cursor, + int limit, + CancellationToken cancellationToken) + => Task.FromResult>(Array.Empty()); + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Observations/IAdvisoryObservationSink.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Observations/IAdvisoryObservationSink.cs new file mode 100644 index 000000000..bc4f65849 --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Observations/IAdvisoryObservationSink.cs @@ -0,0 +1,10 @@ +using System.Threading; +using System.Threading.Tasks; +using StellaOps.Concelier.Models.Observations; + +namespace StellaOps.Concelier.Core.Observations; + +public interface IAdvisoryObservationSink +{ + Task UpsertAsync(AdvisoryObservation observation, CancellationToken cancellationToken); +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Raw/AdvisoryRawService.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Raw/AdvisoryRawService.cs index 0c21d58b1..a485fb20a 100644 --- a/src/Concelier/__Libraries/StellaOps.Concelier.Core/Raw/AdvisoryRawService.cs +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Core/Raw/AdvisoryRawService.cs @@ -9,7 +9,8 @@ using Microsoft.Extensions.Logging; using StellaOps.Aoc; using StellaOps.Ingestion.Telemetry; using StellaOps.Concelier.Core.Aoc; -using StellaOps.Concelier.Core.Linksets; +using StellaOps.Concelier.Core.Linksets; +using StellaOps.Concelier.Core.Observations; using StellaOps.Concelier.RawModels; using StellaOps.Concelier.Models; @@ -19,28 +20,37 @@ internal sealed class AdvisoryRawService : IAdvisoryRawService { private static readonly ImmutableArray EmptyArray = ImmutableArray.Empty; - private readonly IAdvisoryRawRepository _repository; - private readonly IAdvisoryRawWriteGuard _writeGuard; - private readonly IAocGuard _aocGuard; - private readonly IAdvisoryLinksetMapper _linksetMapper; - private readonly TimeProvider _timeProvider; - private readonly ILogger _logger; + private readonly IAdvisoryRawRepository _repository; + private readonly IAdvisoryRawWriteGuard _writeGuard; + private readonly IAocGuard _aocGuard; + private readonly IAdvisoryLinksetMapper _linksetMapper; + private readonly IAdvisoryObservationFactory _observationFactory; + private readonly IAdvisoryObservationSink _observationSink; + private readonly IAdvisoryLinksetSink _linksetSink; + private readonly TimeProvider _timeProvider; + private readonly ILogger _logger; public AdvisoryRawService( - IAdvisoryRawRepository repository, - IAdvisoryRawWriteGuard writeGuard, - IAocGuard aocGuard, - IAdvisoryLinksetMapper linksetMapper, - TimeProvider timeProvider, - ILogger logger) - { - _repository = repository ?? throw new ArgumentNullException(nameof(repository)); - _writeGuard = writeGuard ?? throw new ArgumentNullException(nameof(writeGuard)); - _aocGuard = aocGuard ?? throw new ArgumentNullException(nameof(aocGuard)); - _linksetMapper = linksetMapper ?? throw new ArgumentNullException(nameof(linksetMapper)); - _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + IAdvisoryRawRepository repository, + IAdvisoryRawWriteGuard writeGuard, + IAocGuard aocGuard, + IAdvisoryLinksetMapper linksetMapper, + IAdvisoryObservationFactory observationFactory, + IAdvisoryObservationSink observationSink, + IAdvisoryLinksetSink linksetSink, + TimeProvider timeProvider, + ILogger logger) + { + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + _writeGuard = writeGuard ?? throw new ArgumentNullException(nameof(writeGuard)); + _aocGuard = aocGuard ?? throw new ArgumentNullException(nameof(aocGuard)); + _linksetMapper = linksetMapper ?? throw new ArgumentNullException(nameof(linksetMapper)); + _observationFactory = observationFactory ?? throw new ArgumentNullException(nameof(observationFactory)); + _observationSink = observationSink ?? throw new ArgumentNullException(nameof(observationSink)); + _linksetSink = linksetSink ?? throw new ArgumentNullException(nameof(linksetSink)); + _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } public async Task IngestAsync(AdvisoryRawDocument document, CancellationToken cancellationToken) { @@ -102,6 +112,23 @@ internal sealed class AdvisoryRawService : IAdvisoryRawService var result = await _repository.UpsertAsync(enriched, cancellationToken).ConfigureAwait(false); IngestionTelemetry.RecordWriteAttempt(tenant, source, result.Inserted ? IngestionTelemetry.ResultOk : IngestionTelemetry.ResultNoop); + // Persist observation + linkset for Link-Not-Merge consumers (idempotent upserts). + var observation = _observationFactory.Create(enriched, _timeProvider.GetUtcNow()); + await _observationSink.UpsertAsync(observation, cancellationToken).ConfigureAwait(false); + + var normalizedLinkset = AdvisoryLinksetNormalization.FromRawLinkset(enriched.Linkset); + var linkset = new AdvisoryLinkset( + tenant, + source, + enriched.Upstream.UpstreamId, + ImmutableArray.Create(observation.ObservationId), + normalizedLinkset, + null, + _timeProvider.GetUtcNow(), + null); + + await _linksetSink.UpsertAsync(linkset, cancellationToken).ConfigureAwait(false); + if (result.Inserted) { _logger.LogInformation( diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetDocument.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetDocument.cs new file mode 100644 index 000000000..5804b7982 --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetDocument.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace StellaOps.Concelier.Storage.Mongo.Linksets; + +[BsonIgnoreExtraElements] +public sealed class AdvisoryLinksetDocument +{ + [BsonId] + public ObjectId Id { get; set; } + = ObjectId.GenerateNewId(); + + [BsonElement("tenantId")] + public string TenantId { get; set; } = string.Empty; + + [BsonElement("source")] + public string Source { get; set; } = string.Empty; + + [BsonElement("advisoryId")] + public string AdvisoryId { get; set; } = string.Empty; + + [BsonElement("observations")] + public List Observations { get; set; } = new(); + + [BsonElement("normalized")] + [BsonIgnoreIfNull] + public AdvisoryLinksetNormalizedDocument? Normalized { get; set; } + = null; + + [BsonElement("createdAt")] + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + [BsonElement("builtByJobId")] + [BsonIgnoreIfNull] + public string? BuiltByJobId { get; set; } + = null; + + [BsonElement("provenance")] + [BsonIgnoreIfNull] + public AdvisoryLinksetProvenanceDocument? Provenance { get; set; } + = null; +} + +[BsonIgnoreExtraElements] +public sealed class AdvisoryLinksetNormalizedDocument +{ + [BsonElement("purls")] + [BsonIgnoreIfNull] + public List? Purls { get; set; } + = new(); + + [BsonElement("versions")] + [BsonIgnoreIfNull] + public List? Versions { get; set; } + = new(); + + [BsonElement("ranges")] + [BsonIgnoreIfNull] + public List? Ranges { get; set; } + = new(); + + [BsonElement("severities")] + [BsonIgnoreIfNull] + public List? Severities { get; set; } + = new(); +} + +[BsonIgnoreExtraElements] +public sealed class AdvisoryLinksetProvenanceDocument +{ + [BsonElement("observationHashes")] + [BsonIgnoreIfNull] + public List? ObservationHashes { get; set; } + = new(); + + [BsonElement("toolVersion")] + [BsonIgnoreIfNull] + public string? ToolVersion { get; set; } + = null; + + [BsonElement("policyHash")] + [BsonIgnoreIfNull] + public string? PolicyHash { get; set; } + = null; +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetSink.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetSink.cs new file mode 100644 index 000000000..5e173c8aa --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetSink.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CoreLinksets = StellaOps.Concelier.Core.Linksets; + +namespace StellaOps.Concelier.Storage.Mongo.Linksets; + +internal sealed class AdvisoryLinksetSink : CoreLinksets.IAdvisoryLinksetSink +{ + private readonly IAdvisoryLinksetStore _store; + + public AdvisoryLinksetSink(IAdvisoryLinksetStore store) + { + _store = store ?? throw new ArgumentNullException(nameof(store)); + } + + public Task UpsertAsync(CoreLinksets.AdvisoryLinkset linkset, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(linkset); + return _store.UpsertAsync(linkset, cancellationToken); + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetStore.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetStore.cs new file mode 100644 index 000000000..634d17161 --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Linksets/AdvisoryLinksetStore.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver; +using CoreLinksets = StellaOps.Concelier.Core.Linksets; + +namespace StellaOps.Concelier.Storage.Mongo.Linksets; + +// Internal type kept in storage namespace to avoid name clash with core interface +internal sealed class MongoAdvisoryLinksetStore : CoreLinksets.IAdvisoryLinksetStore, CoreLinksets.IAdvisoryLinksetLookup +{ + private readonly IMongoCollection _collection; + + public MongoAdvisoryLinksetStore(IMongoCollection collection) + { + _collection = collection ?? throw new ArgumentNullException(nameof(collection)); + } + + public async Task UpsertAsync(CoreLinksets.AdvisoryLinkset linkset, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(linkset); + + var document = MapToDocument(linkset); + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.TenantId, linkset.TenantId), + Builders.Filter.Eq(d => d.Source, linkset.Source), + Builders.Filter.Eq(d => d.AdvisoryId, linkset.AdvisoryId)); + + var options = new ReplaceOptions { IsUpsert = true }; + await _collection.ReplaceOneAsync(filter, document, options, cancellationToken).ConfigureAwait(false); + } + + public async Task> FindByTenantAsync( + string tenantId, + IEnumerable? advisoryIds, + IEnumerable? sources, + CoreLinksets.AdvisoryLinksetCursor? cursor, + int limit, + CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrWhiteSpace(tenantId); + if (limit <= 0) + { + throw new ArgumentOutOfRangeException(nameof(limit)); + } + + var builder = Builders.Filter; + var filters = new List> + { + builder.Eq(d => d.TenantId, tenantId.ToLowerInvariant()) + }; + + if (advisoryIds is not null) + { + var ids = advisoryIds.Where(v => !string.IsNullOrWhiteSpace(v)).ToArray(); + if (ids.Length > 0) + { + filters.Add(builder.In(d => d.AdvisoryId, ids)); + } + } + + if (sources is not null) + { + var srcs = sources.Where(v => !string.IsNullOrWhiteSpace(v)).ToArray(); + if (srcs.Length > 0) + { + filters.Add(builder.In(d => d.Source, srcs)); + } + } + + var filter = builder.And(filters); + + var sort = Builders.Sort.Descending(d => d.CreatedAt).Ascending(d => d.AdvisoryId); + var findFilter = filter; + + if (cursor is not null) + { + var cursorFilter = builder.Or( + builder.Lt(d => d.CreatedAt, cursor.CreatedAt.UtcDateTime), + builder.And( + builder.Eq(d => d.CreatedAt, cursor.CreatedAt.UtcDateTime), + builder.Gt(d => d.AdvisoryId, cursor.AdvisoryId))); + + findFilter = builder.And(findFilter, cursorFilter); + } + + var documents = await _collection.Find(findFilter) + .Sort(sort) + .Limit(limit) + .ToListAsync(cancellationToken) + .ConfigureAwait(false); + + return documents.Select(FromDocument).ToArray(); + } + + private static AdvisoryLinksetDocument MapToDocument(CoreLinksets.AdvisoryLinkset linkset) + { + var doc = new AdvisoryLinksetDocument + { + TenantId = linkset.TenantId, + Source = linkset.Source, + AdvisoryId = linkset.AdvisoryId, + Observations = new List(linkset.ObservationIds), + CreatedAt = linkset.CreatedAt.UtcDateTime, + BuiltByJobId = linkset.BuiltByJobId, + Provenance = linkset.Provenance is null ? null : new AdvisoryLinksetProvenanceDocument + { + ObservationHashes = linkset.Provenance.ObservationHashes is null + ? null + : new List(linkset.Provenance.ObservationHashes), + ToolVersion = linkset.Provenance.ToolVersion, + PolicyHash = linkset.Provenance.PolicyHash, + }, + Normalized = linkset.Normalized is null ? null : new AdvisoryLinksetNormalizedDocument + { + Purls = linkset.Normalized.Purls is null ? null : new List(linkset.Normalized.Purls), + Versions = linkset.Normalized.Versions is null ? null : new List(linkset.Normalized.Versions), + Ranges = linkset.Normalized.RangesToBson(), + Severities = linkset.Normalized.SeveritiesToBson(), + } + }; + + return doc; + } + + private static CoreLinksets.AdvisoryLinkset FromDocument(AdvisoryLinksetDocument doc) + { + return new AdvisoryLinkset( + doc.TenantId, + doc.Source, + doc.AdvisoryId, + doc.Observations.ToImmutableArray(), + doc.Normalized is null ? null : new AdvisoryLinksetNormalized( + doc.Normalized.Purls, + doc.Normalized.Versions, + doc.Normalized.Ranges?.Select(ToDictionary).ToList(), + doc.Normalized.Severities?.Select(ToDictionary).ToList()), + doc.Provenance is null ? null : new AdvisoryLinksetProvenance( + doc.Provenance.ObservationHashes, + doc.Provenance.ToolVersion, + doc.Provenance.PolicyHash), + DateTime.SpecifyKind(doc.CreatedAt, DateTimeKind.Utc), + doc.BuiltByJobId); + } + + private static Dictionary ToDictionary(MongoDB.Bson.BsonDocument bson) + { + var dict = new Dictionary(StringComparer.Ordinal); + foreach (var element in bson.Elements) + { + dict[element.Name] = element.Value switch + { + MongoDB.Bson.BsonString s => s.AsString, + MongoDB.Bson.BsonInt32 i => i.AsInt32, + MongoDB.Bson.BsonInt64 l => l.AsInt64, + MongoDB.Bson.BsonDouble d => d.AsDouble, + MongoDB.Bson.BsonDecimal128 dec => dec.ToDecimal(), + MongoDB.Bson.BsonBoolean b => b.AsBoolean, + MongoDB.Bson.BsonDateTime dt => dt.ToUniversalTime(), + MongoDB.Bson.BsonNull => (object?)null, + MongoDB.Bson.BsonArray arr => arr.Select(v => v.ToString()).ToArray(), + _ => element.Value.ToString() + }; + } + return dict; + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Migrations/EnsureLinkNotMergeCollectionsMigration.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Migrations/EnsureLinkNotMergeCollectionsMigration.cs new file mode 100644 index 000000000..4775236c6 --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Migrations/EnsureLinkNotMergeCollectionsMigration.cs @@ -0,0 +1,242 @@ +using System.Collections.Generic; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace StellaOps.Concelier.Storage.Mongo.Migrations; + +internal sealed class EnsureLinkNotMergeCollectionsMigration : IMongoMigration +{ + public string Id => "20251116_link_not_merge_collections"; + + public string Description => "Ensure advisory_observations and advisory_linksets collections exist with validators and indexes for Link-Not-Merge"; + + public async Task ApplyAsync(IMongoDatabase database, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(database); + + await EnsureObservationsAsync(database, cancellationToken).ConfigureAwait(false); + await EnsureLinksetsAsync(database, cancellationToken).ConfigureAwait(false); + } + + private static async Task EnsureObservationsAsync(IMongoDatabase database, CancellationToken ct) + { + var collectionName = MongoStorageDefaults.Collections.AdvisoryObservations; + var validator = new BsonDocument("$jsonSchema", BuildObservationSchema()); + await EnsureCollectionWithValidatorAsync(database, collectionName, validator, ct).ConfigureAwait(false); + + var collection = database.GetCollection(collectionName); + var indexes = new List> + { + new(new BsonDocument + { + {"tenant", 1}, + {"source", 1}, + {"advisoryId", 1}, + {"upstream.fetchedAt", -1}, + }, + new CreateIndexOptions { Name = "obs_tenant_source_adv_fetchedAt" }), + new(new BsonDocument + { + {"provenance.sourceArtifactSha", 1}, + }, + new CreateIndexOptions { Name = "obs_prov_sourceArtifactSha_unique", Unique = true }), + }; + + await collection.Indexes.CreateManyAsync(indexes, cancellationToken: ct).ConfigureAwait(false); + } + + private static async Task EnsureLinksetsAsync(IMongoDatabase database, CancellationToken ct) + { + var collectionName = MongoStorageDefaults.Collections.AdvisoryLinksets; + var validator = new BsonDocument("$jsonSchema", BuildLinksetSchema()); + await EnsureCollectionWithValidatorAsync(database, collectionName, validator, ct).ConfigureAwait(false); + + var collection = database.GetCollection(collectionName); + var indexes = new List> + { + new(new BsonDocument + { + {"tenantId", 1}, + {"advisoryId", 1}, + {"source", 1}, + }, + new CreateIndexOptions { Name = "linkset_tenant_advisory_source", Unique = true }), + new(new BsonDocument { { "observations", 1 } }, new CreateIndexOptions { Name = "linkset_observations" }) + }; + + await collection.Indexes.CreateManyAsync(indexes, cancellationToken: ct).ConfigureAwait(false); + } + + private static async Task EnsureCollectionWithValidatorAsync( + IMongoDatabase database, + string collectionName, + BsonDocument validator, + CancellationToken ct) + { + var filter = new BsonDocument("name", collectionName); + var existing = await database.ListCollectionsAsync(new ListCollectionsOptions { Filter = filter }, ct) + .ConfigureAwait(false); + var exists = await existing.AnyAsync(ct).ConfigureAwait(false); + + if (!exists) + { + var options = new CreateCollectionOptions + { + Validator = validator, + ValidationLevel = DocumentValidationLevel.Moderate, + ValidationAction = DocumentValidationAction.Error, + }; + + await database.CreateCollectionAsync(collectionName, options, ct).ConfigureAwait(false); + } + else + { + var command = new BsonDocument + { + { "collMod", collectionName }, + { "validator", validator }, + { "validationLevel", "moderate" }, + { "validationAction", "error" }, + }; + await database.RunCommandAsync(command, cancellationToken: ct).ConfigureAwait(false); + } + } + + private static BsonDocument BuildObservationSchema() + { + return new BsonDocument + { + { "bsonType", "object" }, + { "required", new BsonArray { "_id", "tenantId", "source", "advisoryId", "affected", "provenance", "ingestedAt" } }, + { "properties", new BsonDocument + { + { "_id", new BsonDocument("bsonType", "string") }, + { "tenantId", new BsonDocument("bsonType", "string") }, + { "source", new BsonDocument("bsonType", "string") }, + { "advisoryId", new BsonDocument("bsonType", "string") }, + { "title", new BsonDocument("bsonType", new BsonArray { "string", "null" }) }, + { "summary", new BsonDocument("bsonType", new BsonArray { "string", "null" }) }, + { "severities", new BsonDocument + { + { "bsonType", "array" }, + { "items", new BsonDocument + { + { "bsonType", "object" }, + { "required", new BsonArray { "system", "score" } }, + { "properties", new BsonDocument + { + { "system", new BsonDocument("bsonType", "string") }, + { "score", new BsonDocument("bsonType", new BsonArray { "double", "int", "long", "decimal" }) }, + { "vector", new BsonDocument("bsonType", new BsonArray { "string", "null" }) } + } + } + } + } + } + }, + { "affected", new BsonDocument + { + { "bsonType", "array" }, + { "items", new BsonDocument + { + { "bsonType", "object" }, + { "required", new BsonArray { "purl" } }, + { "properties", new BsonDocument + { + { "purl", new BsonDocument("bsonType", "string") }, + { "package", new BsonDocument("bsonType", new BsonArray { "string", "null" }) }, + { "versions", new BsonDocument("bsonType", new BsonArray { "array", "null" }) }, + { "ranges", new BsonDocument("bsonType", new BsonArray { "array", "null" }) }, + { "ecosystem", new BsonDocument("bsonType", new BsonArray { "string", "null" }) }, + { "cpe", new BsonDocument("bsonType", new BsonArray { "array", "null" }) }, + { "cpes", new BsonDocument("bsonType", new BsonArray { "array", "null" }) } + } + } + } + } + } + }, + { "references", new BsonDocument + { + { "bsonType", new BsonArray { "array", "null" } }, + { "items", new BsonDocument("bsonType", "string") } + } + }, + { "weaknesses", new BsonDocument + { + { "bsonType", new BsonArray { "array", "null" } }, + { "items", new BsonDocument("bsonType", "string") } + } + }, + { "published", new BsonDocument("bsonType", new BsonArray { "date", "null" }) }, + { "modified", new BsonDocument("bsonType", new BsonArray { "date", "null" }) }, + { "provenance", new BsonDocument + { + { "bsonType", "object" }, + { "required", new BsonArray { "sourceArtifactSha", "fetchedAt" } }, + { "properties", new BsonDocument + { + { "sourceArtifactSha", new BsonDocument("bsonType", "string") }, + { "fetchedAt", new BsonDocument("bsonType", "date") }, + { "ingestJobId", new BsonDocument("bsonType", new BsonArray { "string", "null" }) }, + { "signature", new BsonDocument("bsonType", new BsonArray { "object", "null" }) } + } + } + } + }, + { "ingestedAt", new BsonDocument("bsonType", "date") } + } + } + }; + } + + private static BsonDocument BuildLinksetSchema() + { + return new BsonDocument + { + { "bsonType", "object" }, + { "required", new BsonArray { "_id", "tenantId", "source", "advisoryId", "observations", "createdAt" } }, + { "properties", new BsonDocument + { + { "_id", new BsonDocument("bsonType", "objectId") }, + { "tenantId", new BsonDocument("bsonType", "string") }, + { "source", new BsonDocument("bsonType", "string") }, + { "advisoryId", new BsonDocument("bsonType", "string") }, + { "observations", new BsonDocument + { + { "bsonType", "array" }, + { "items", new BsonDocument("bsonType", "string") } + } + }, + { "normalized", new BsonDocument + { + { "bsonType", new BsonArray { "object", "null" } }, + { "properties", new BsonDocument + { + { "purls", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "string") } } }, + { "versions", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "string") } } }, + { "ranges", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "object") } } }, + { "severities", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "object") } } } + } + } + } + }, + { "createdAt", new BsonDocument("bsonType", "date") }, + { "builtByJobId", new BsonDocument("bsonType", new BsonArray { "string", "null" }) }, + { "provenance", new BsonDocument + { + { "bsonType", new BsonArray { "object", "null" } }, + { "properties", new BsonDocument + { + { "observationHashes", new BsonDocument { { "bsonType", new BsonArray { "array", "null" } }, { "items", new BsonDocument("bsonType", "string") } } }, + { "toolVersion", new BsonDocument("bsonType", new BsonArray { "string", "null" }) }, + { "policyHash", new BsonDocument("bsonType", new BsonArray { "string", "null" }) } + } + } + } + } + } + } + }; + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Observations/AdvisoryObservationSink.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Observations/AdvisoryObservationSink.cs new file mode 100644 index 000000000..2ccba46df --- /dev/null +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/Observations/AdvisoryObservationSink.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using StellaOps.Concelier.Core.Observations; +using StellaOps.Concelier.Models.Observations; + +namespace StellaOps.Concelier.Storage.Mongo.Observations; + +internal sealed class AdvisoryObservationSink : IAdvisoryObservationSink +{ + private readonly IAdvisoryObservationStore _store; + + public AdvisoryObservationSink(IAdvisoryObservationStore store) + { + _store = store ?? throw new ArgumentNullException(nameof(store)); + } + + public Task UpsertAsync(AdvisoryObservation observation, CancellationToken cancellationToken) + { + return _store.UpsertAsync(observation, cancellationToken); + } +} diff --git a/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/ServiceCollectionExtensions.cs b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/ServiceCollectionExtensions.cs index f8ca2bc7c..8eb812190 100644 --- a/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/ServiceCollectionExtensions.cs +++ b/src/Concelier/__Libraries/StellaOps.Concelier.Storage.Mongo/ServiceCollectionExtensions.cs @@ -80,6 +80,13 @@ public static class ServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(sp => + sp.GetRequiredService()); + services.AddSingleton(sp => + sp.GetRequiredService()); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.TryAddSingleton(); diff --git a/src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/Linksets/AdvisoryLinksetQueryServiceTests.cs b/src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/Linksets/AdvisoryLinksetQueryServiceTests.cs new file mode 100644 index 000000000..403ebcb73 --- /dev/null +++ b/src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/Linksets/AdvisoryLinksetQueryServiceTests.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using StellaOps.Concelier.Core.Linksets; +using Xunit; + +namespace StellaOps.Concelier.Core.Tests.Linksets; + +public sealed class AdvisoryLinksetQueryServiceTests +{ + [Fact] + public async Task QueryAsync_ReturnsPagedResults_WithCursor() + { + var linksets = new List + { + new("tenant", "ghsa", "adv-003", + ImmutableArray.Create("obs-003"), + new AdvisoryLinksetNormalized(new[]{"pkg:npm/a"}, new[]{"1.0.0"}, null, null), + null, DateTimeOffset.Parse("2025-11-10T12:00:00Z"), null), + new("tenant", "ghsa", "adv-002", + ImmutableArray.Create("obs-002"), + new AdvisoryLinksetNormalized(new[]{"pkg:npm/b"}, new[]{"2.0.0"}, null, null), + null, DateTimeOffset.Parse("2025-11-09T12:00:00Z"), null), + new("tenant", "ghsa", "adv-001", + ImmutableArray.Create("obs-001"), + new AdvisoryLinksetNormalized(new[]{"pkg:npm/c"}, new[]{"3.0.0"}, null, null), + null, DateTimeOffset.Parse("2025-11-08T12:00:00Z"), null), + }; + + var lookup = new FakeLinksetLookup(linksets); + var service = new AdvisoryLinksetQueryService(lookup); + + var firstPage = await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", limit: 2), CancellationToken.None); + + Assert.Equal(2, firstPage.Linksets.Length); + Assert.True(firstPage.HasMore); + Assert.False(string.IsNullOrWhiteSpace(firstPage.NextCursor)); + Assert.Equal("adv-003", firstPage.Linksets[0].AdvisoryId); + Assert.Equal("pkg:npm/a", firstPage.Linksets[0].Normalized?.Purls?.First()); + + var secondPage = await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", limit: 2, Cursor: firstPage.NextCursor), CancellationToken.None); + + Assert.Single(secondPage.Linksets); + Assert.False(secondPage.HasMore); + Assert.Null(secondPage.NextCursor); + Assert.Equal("adv-001", secondPage.Linksets[0].AdvisoryId); + } + + [Fact] + public async Task QueryAsync_InvalidCursor_ThrowsFormatException() + { + var lookup = new FakeLinksetLookup(Array.Empty()); + var service = new AdvisoryLinksetQueryService(lookup); + + await Assert.ThrowsAsync(async () => + { + await service.QueryAsync(new AdvisoryLinksetQueryOptions("tenant", limit: 1, Cursor: "not-base64"), CancellationToken.None); + }); + } + + private sealed class FakeLinksetLookup : IAdvisoryLinksetLookup + { + private readonly IReadOnlyList _linksets; + + public FakeLinksetLookup(IReadOnlyList linksets) + { + _linksets = linksets; + } + + public Task> FindByTenantAsync( + string tenantId, + IEnumerable? advisoryIds, + IEnumerable? sources, + AdvisoryLinksetCursor? cursor, + int limit, + CancellationToken cancellationToken) + { + var ordered = _linksets + .Where(ls => ls.TenantId == tenantId) + .OrderByDescending(ls => ls.CreatedAt) + .ThenBy(ls => ls.AdvisoryId, StringComparer.Ordinal) + .ToList(); + + if (cursor is not null) + { + ordered = ordered + .Where(ls => ls.CreatedAt < cursor.CreatedAt || + (ls.CreatedAt == cursor.CreatedAt && string.Compare(ls.AdvisoryId, cursor.AdvisoryId, StringComparison.Ordinal) > 0)) + .ToList(); + } + + return Task.FromResult>(ordered.Take(limit).ToList()); + } + } +} diff --git a/src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/WebServiceEndpointsTests.cs b/src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/WebServiceEndpointsTests.cs index 6e5f67895..0004ba81c 100644 --- a/src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/WebServiceEndpointsTests.cs +++ b/src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/WebServiceEndpointsTests.cs @@ -205,6 +205,104 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime Assert.Equal("tenant-a:nvd:alpha:1", secondObservations[0].GetProperty("observationId").GetString()); } + [Fact] + public async Task LinksetsEndpoint_ReturnsNormalizedLinksetsFromIngestion() + { + var tenant = "tenant-linkset-ingest"; + using var client = _factory.CreateClient(); + client.DefaultRequestHeaders.Add("X-Stella-Tenant", tenant); + + var firstIngest = await client.PostAsJsonAsync("/ingest/advisory", BuildAdvisoryIngestRequest("sha256:linkset-1", "GHSA-LINK-001", purls: new[] { "pkg:npm/demo@1.0.0" })); + firstIngest.EnsureSuccessStatusCode(); + + var secondIngest = await client.PostAsJsonAsync("/ingest/advisory", BuildAdvisoryIngestRequest("sha256:linkset-2", "GHSA-LINK-002", purls: new[] { "pkg:npm/demo@2.0.0" })); + secondIngest.EnsureSuccessStatusCode(); + + var response = await client.GetAsync("/linksets?tenant=tenant-linkset-ingest&limit=10"); + response.EnsureSuccessStatusCode(); + + var payload = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(payload); + Assert.Equal(2, payload!.Linksets.Length); + + var linksetAdvisoryIds = payload.Linksets.Select(ls => ls.AdvisoryId).OrderBy(id => id, StringComparer.Ordinal).ToArray(); + Assert.Equal(new[] { "GHSA-LINK-001", "GHSA-LINK-002" }, linksetAdvisoryIds); + + var allPurls = payload.Linksets.SelectMany(ls => ls.Purls).OrderBy(p => p, StringComparer.Ordinal).ToArray(); + Assert.Contains("pkg:npm/demo@1.0.0", allPurls); + Assert.Contains("pkg:npm/demo@2.0.0", allPurls); + + var versions = payload.Linksets + .SelectMany(ls => ls.Versions) + .Distinct(StringComparer.Ordinal) + .OrderBy(v => v, StringComparer.Ordinal) + .ToArray(); + Assert.Contains("1.0.0", versions); + Assert.Contains("2.0.0", versions); + + Assert.False(payload.HasMore); + Assert.True(string.IsNullOrEmpty(payload.NextCursor)); + } + + [Fact] + public async Task LinksetsEndpoint_SupportsCursorPagination() + { + var tenant = "tenant-linkset-page"; + var documents = new[] + { + CreateLinksetDocument( + tenant, + "nvd", + "ADV-002", + new[] { "obs-2" }, + new[] { "pkg:npm/demo@2.0.0" }, + new[] { "2.0.0" }, + new DateTime(2025, 1, 6, 0, 0, 0, DateTimeKind.Utc)), + CreateLinksetDocument( + tenant, + "osv", + "ADV-001", + new[] { "obs-1" }, + new[] { "pkg:npm/demo@1.0.0" }, + new[] { "1.0.0" }, + new DateTime(2025, 1, 5, 0, 0, 0, DateTimeKind.Utc)), + CreateLinksetDocument( + "tenant-other", + "osv", + "ADV-999", + new[] { "obs-x" }, + new[] { "pkg:npm/other@1.0.0" }, + new[] { "1.0.0" }, + new DateTime(2025, 1, 4, 0, 0, 0, DateTimeKind.Utc)) + }; + + await SeedLinksetDocumentsAsync(documents); + + using var client = _factory.CreateClient(); + + var firstResponse = await client.GetAsync($"/linksets?tenant={tenant}&limit=1"); + firstResponse.EnsureSuccessStatusCode(); + var firstPayload = await firstResponse.Content.ReadFromJsonAsync(); + Assert.NotNull(firstPayload); + var first = Assert.Single(firstPayload!.Linksets); + Assert.Equal("ADV-002", first.AdvisoryId); + Assert.Equal(new[] { "pkg:npm/demo@2.0.0" }, first.Purls.ToArray()); + Assert.Equal(new[] { "2.0.0" }, first.Versions.ToArray()); + Assert.True(firstPayload.HasMore); + Assert.False(string.IsNullOrWhiteSpace(firstPayload.NextCursor)); + + var secondResponse = await client.GetAsync($"/linksets?tenant={tenant}&limit=1&cursor={Uri.EscapeDataString(firstPayload.NextCursor!)}"); + secondResponse.EnsureSuccessStatusCode(); + var secondPayload = await secondResponse.Content.ReadFromJsonAsync(); + Assert.NotNull(secondPayload); + var second = Assert.Single(secondPayload!.Linksets); + Assert.Equal("ADV-001", second.AdvisoryId); + Assert.Equal(new[] { "pkg:npm/demo@1.0.0" }, second.Purls.ToArray()); + Assert.Equal(new[] { "1.0.0" }, second.Versions.ToArray()); + Assert.False(secondPayload.HasMore); + Assert.True(string.IsNullOrEmpty(secondPayload.NextCursor)); + } + [Fact] public async Task ObservationsEndpoint_ReturnsBadRequestWhenTenantMissing() { @@ -1505,6 +1603,52 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime await SeedAdvisoryRawDocumentsAsync(rawDocuments); } + private async Task SeedLinksetDocumentsAsync(IEnumerable documents) + { + var client = new MongoClient(_runner.ConnectionString); + var database = client.GetDatabase(MongoStorageDefaults.DefaultDatabaseName); + var collection = database.GetCollection(MongoStorageDefaults.Collections.AdvisoryLinksets); + + try + { + await database.DropCollectionAsync(MongoStorageDefaults.Collections.AdvisoryLinksets); + } + catch (MongoCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase)) + { + // Collection not created yet; safe to ignore. + } + + var snapshot = documents?.ToArray() ?? Array.Empty(); + if (snapshot.Length > 0) + { + await collection.InsertManyAsync(snapshot); + } + } + + private static AdvisoryLinksetDocument CreateLinksetDocument( + string tenant, + string source, + string advisoryId, + IEnumerable observationIds, + IEnumerable purls, + IEnumerable versions, + DateTime createdAtUtc) + { + return new AdvisoryLinksetDocument + { + TenantId = tenant, + Source = source, + AdvisoryId = advisoryId, + Observations = observationIds.ToList(), + CreatedAt = DateTime.SpecifyKind(createdAtUtc, DateTimeKind.Utc), + Normalized = new AdvisoryLinksetNormalizedDocument + { + Purls = purls.ToList(), + Versions = versions.ToList() + } + }; + } + private static AdvisoryObservationDocument[] BuildSampleObservationDocuments() { return new[] diff --git a/src/Excititor/StellaOps.Excititor.WebService/Contracts/VexEvidenceChunkContracts.cs b/src/Excititor/StellaOps.Excititor.WebService/Contracts/VexEvidenceChunkContracts.cs new file mode 100644 index 000000000..aa374787f --- /dev/null +++ b/src/Excititor/StellaOps.Excititor.WebService/Contracts/VexEvidenceChunkContracts.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace StellaOps.Excititor.WebService.Contracts; + +public sealed record VexEvidenceChunkResponse( + [property: JsonPropertyName("observationId")] string ObservationId, + [property: JsonPropertyName("linksetId")] string LinksetId, + [property: JsonPropertyName("vulnerabilityId")] string VulnerabilityId, + [property: JsonPropertyName("productKey")] string ProductKey, + [property: JsonPropertyName("providerId")] string ProviderId, + [property: JsonPropertyName("status")] string Status, + [property: JsonPropertyName("justification")] string? Justification, + [property: JsonPropertyName("detail")] string? Detail, + [property: JsonPropertyName("scopeScore")] double? ScopeScore, + [property: JsonPropertyName("firstSeen")] DateTimeOffset FirstSeen, + [property: JsonPropertyName("lastSeen")] DateTimeOffset LastSeen, + [property: JsonPropertyName("scope")] VexEvidenceChunkScope Scope, + [property: JsonPropertyName("document")] VexEvidenceChunkDocument Document, + [property: JsonPropertyName("signature")] VexEvidenceChunkSignature? Signature, + [property: JsonPropertyName("metadata")] IReadOnlyDictionary Metadata); + +public sealed record VexEvidenceChunkScope( + [property: JsonPropertyName("key")] string Key, + [property: JsonPropertyName("name")] string? Name, + [property: JsonPropertyName("version")] string? Version, + [property: JsonPropertyName("purl")] string? Purl, + [property: JsonPropertyName("cpe")] string? Cpe, + [property: JsonPropertyName("componentIdentifiers")] IReadOnlyList ComponentIdentifiers); + +public sealed record VexEvidenceChunkDocument( + [property: JsonPropertyName("digest")] string Digest, + [property: JsonPropertyName("format")] string Format, + [property: JsonPropertyName("sourceUri")] string SourceUri, + [property: JsonPropertyName("revision")] string? Revision); + +public sealed record VexEvidenceChunkSignature( + [property: JsonPropertyName("type")] string Type, + [property: JsonPropertyName("subject")] string? Subject, + [property: JsonPropertyName("issuer")] string? Issuer, + [property: JsonPropertyName("keyId")] string? KeyId, + [property: JsonPropertyName("verifiedAt")] DateTimeOffset? VerifiedAt, + [property: JsonPropertyName("transparencyRef")] string? TransparencyRef); diff --git a/src/Excititor/StellaOps.Excititor.WebService/Program.cs b/src/Excititor/StellaOps.Excititor.WebService/Program.cs index e4b3770a4..1d686123c 100644 --- a/src/Excititor/StellaOps.Excititor.WebService/Program.cs +++ b/src/Excititor/StellaOps.Excititor.WebService/Program.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Collections.Immutable; using System.Globalization; using System.Text; +using System.Text.Json; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -51,11 +52,12 @@ services.AddOptions() services.AddScoped(); services.AddExcititorAocGuards(); services.AddVexExportEngine(); -services.AddVexExportCacheServices(); +services.AddVexExportCacheServices(); services.AddVexAttestation(); services.Configure(configuration.GetSection("Excititor:Attestation:Client")); services.Configure(configuration.GetSection("Excititor:Attestation:Verification")); -services.AddVexPolicy(); +services.AddVexPolicy(); +services.AddSingleton(); services.AddRedHatCsafConnector(); services.Configure(configuration.GetSection(MirrorDistributionOptions.SectionName)); services.AddSingleton(); @@ -515,6 +517,69 @@ app.MapGet("/v1/vex/observations/{vulnerabilityId}/{productKey}", async ( return Results.Json(response); }); +app.MapGet("/v1/vex/evidence/chunks", async ( + HttpContext context, + [FromServices] IVexEvidenceChunkService chunkService, + [FromServices] IOptions storageOptions, + CancellationToken cancellationToken) => +{ + var scopeResult = ScopeAuthorization.RequireScope(context, "vex.read"); + if (scopeResult is not null) + { + return scopeResult; + } + + if (!TryResolveTenant(context, storageOptions.Value, requireHeader: false, out var tenant, out var tenantError)) + { + return tenantError; + } + + var vulnerabilityId = context.Request.Query["vulnerabilityId"].FirstOrDefault(); + var productKey = context.Request.Query["productKey"].FirstOrDefault(); + if (string.IsNullOrWhiteSpace(vulnerabilityId) || string.IsNullOrWhiteSpace(productKey)) + { + return ValidationProblem("vulnerabilityId and productKey are required."); + } + + var providerFilter = BuildStringFilterSet(context.Request.Query["providerId"]); + var statusFilter = BuildStatusFilter(context.Request.Query["status"]); + var since = ParseSinceTimestamp(context.Request.Query["since"]); + var limit = ResolveLimit(context.Request.Query["limit"], defaultValue: 200, min: 1, max: 500); + + var request = new VexEvidenceChunkRequest( + tenant, + vulnerabilityId.Trim(), + productKey.Trim(), + providerFilter, + statusFilter, + since, + limit); + + VexEvidenceChunkResult result; + try + { + result = await chunkService.QueryAsync(request, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + return Results.StatusCode(StatusCodes.Status499ClientClosedRequest); + } + + context.Response.Headers["X-Total-Count"] = result.TotalCount.ToString(CultureInfo.InvariantCulture); + context.Response.Headers["X-Truncated"] = result.Truncated ? "true" : "false"; + context.Response.ContentType = "application/x-ndjson"; + + var options = new JsonSerializerOptions(JsonSerializerDefaults.Web); + foreach (var chunk in result.Chunks) + { + var line = JsonSerializer.Serialize(chunk, options); + await context.Response.WriteAsync(line, cancellationToken).ConfigureAwait(false); + await context.Response.WriteAsync("\n", cancellationToken).ConfigureAwait(false); + } + + return Results.Empty; +}); + app.MapPost("/aoc/verify", async ( HttpContext context, VexAocVerifyRequest? request, diff --git a/src/Excititor/StellaOps.Excititor.WebService/Services/VexEvidenceChunkService.cs b/src/Excititor/StellaOps.Excititor.WebService/Services/VexEvidenceChunkService.cs new file mode 100644 index 000000000..6969a185e --- /dev/null +++ b/src/Excititor/StellaOps.Excititor.WebService/Services/VexEvidenceChunkService.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using StellaOps.Excititor.Core; +using StellaOps.Excititor.Storage.Mongo; +using StellaOps.Excititor.WebService.Contracts; + +namespace StellaOps.Excititor.WebService.Services; + +internal interface IVexEvidenceChunkService +{ + Task QueryAsync(VexEvidenceChunkRequest request, CancellationToken cancellationToken); +} + +internal sealed record VexEvidenceChunkRequest( + string Tenant, + string VulnerabilityId, + string ProductKey, + ImmutableHashSet ProviderIds, + ImmutableHashSet Statuses, + DateTimeOffset? Since, + int Limit); + +internal sealed record VexEvidenceChunkResult( + IReadOnlyList Chunks, + bool Truncated, + int TotalCount, + DateTimeOffset GeneratedAtUtc); + +internal sealed class VexEvidenceChunkService : IVexEvidenceChunkService +{ + private readonly IVexClaimStore _claimStore; + private readonly TimeProvider _timeProvider; + + public VexEvidenceChunkService(IVexClaimStore claimStore, TimeProvider timeProvider) + { + _claimStore = claimStore ?? throw new ArgumentNullException(nameof(claimStore)); + _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider)); + } + + public async Task QueryAsync(VexEvidenceChunkRequest request, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(request); + + var claims = await _claimStore + .FindAsync(request.VulnerabilityId, request.ProductKey, request.Since, cancellationToken) + .ConfigureAwait(false); + + var filtered = claims + .Where(claim => MatchesProvider(claim, request.ProviderIds)) + .Where(claim => MatchesStatus(claim, request.Statuses)) + .OrderByDescending(claim => claim.LastSeen) + .ToList(); + + var total = filtered.Count; + if (filtered.Count > request.Limit) + { + filtered = filtered.Take(request.Limit).ToList(); + } + + var chunks = filtered + .Select(MapChunk) + .ToList(); + + return new VexEvidenceChunkResult( + chunks, + total > request.Limit, + total, + _timeProvider.GetUtcNow()); + } + + private static bool MatchesProvider(VexClaim claim, ImmutableHashSet providers) + => providers.Count == 0 || providers.Contains(claim.ProviderId, StringComparer.OrdinalIgnoreCase); + + private static bool MatchesStatus(VexClaim claim, ImmutableHashSet statuses) + => statuses.Count == 0 || statuses.Contains(claim.Status); + + private static VexEvidenceChunkResponse MapChunk(VexClaim claim) + { + var observationId = string.Create(CultureInfo.InvariantCulture, $"{claim.ProviderId}:{claim.Document.Digest}"); + var linksetId = string.Create(CultureInfo.InvariantCulture, $"{claim.VulnerabilityId}:{claim.Product.Key}"); + + var scope = new VexEvidenceChunkScope( + claim.Product.Key, + claim.Product.Name, + claim.Product.Version, + claim.Product.Purl, + claim.Product.Cpe, + claim.Product.ComponentIdentifiers); + + var document = new VexEvidenceChunkDocument( + claim.Document.Digest, + claim.Document.Format.ToString().ToLowerInvariant(), + claim.Document.SourceUri.ToString(), + claim.Document.Revision); + + var signature = claim.Document.Signature is null + ? null + : new VexEvidenceChunkSignature( + claim.Document.Signature.Type, + claim.Document.Signature.Subject, + claim.Document.Signature.Issuer, + claim.Document.Signature.KeyId, + claim.Document.Signature.VerifiedAt, + claim.Document.Signature.TransparencyLogReference); + + var scopeScore = claim.Confidence?.Score ?? claim.Signals?.Severity?.Score; + + return new VexEvidenceChunkResponse( + observationId, + linksetId, + claim.VulnerabilityId, + claim.Product.Key, + claim.ProviderId, + claim.Status.ToString(), + claim.Justification?.ToString(), + claim.Detail, + scopeScore, + claim.FirstSeen, + claim.LastSeen, + scope, + document, + signature, + claim.AdditionalMetadata); + } +} diff --git a/src/Excititor/__Libraries/StellaOps.Excititor.Attestation/Properties/AssemblyInfo.cs b/src/Excititor/__Libraries/StellaOps.Excititor.Attestation/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..10dbd9655 --- /dev/null +++ b/src/Excititor/__Libraries/StellaOps.Excititor.Attestation/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("StellaOps.Excititor.Attestation.Tests")] diff --git a/src/Excititor/__Libraries/StellaOps.Excititor.Core/Observations/TimelineEvent.cs b/src/Excititor/__Libraries/StellaOps.Excititor.Core/Observations/TimelineEvent.cs new file mode 100644 index 000000000..f3f7deaab --- /dev/null +++ b/src/Excititor/__Libraries/StellaOps.Excititor.Core/Observations/TimelineEvent.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Immutable; + +namespace StellaOps.Excititor.Core.Observations; + +///

+/// Immutable timeline event emitted for ingest/linkset changes with deterministic field ordering. +/// +public sealed record TimelineEvent +{ + public TimelineEvent( + string eventId, + string tenant, + string providerId, + string streamId, + string eventType, + string traceId, + string justificationSummary, + DateTimeOffset createdAt, + string? evidenceHash = null, + string? payloadHash = null, + ImmutableDictionary? attributes = null) + { + EventId = Ensure(eventId, nameof(eventId)); + Tenant = Ensure(tenant, nameof(tenant)).ToLowerInvariant(); + ProviderId = Ensure(providerId, nameof(providerId)); + StreamId = Ensure(streamId, nameof(streamId)); + EventType = Ensure(eventType, nameof(eventType)); + TraceId = Ensure(traceId, nameof(traceId)); + JustificationSummary = justificationSummary?.Trim() ?? string.Empty; + EvidenceHash = evidenceHash?.Trim(); + PayloadHash = payloadHash?.Trim(); + CreatedAt = createdAt; + Attributes = Normalize(attributes); + } + + public string EventId { get; } + public string Tenant { get; } + public string ProviderId { get; } + public string StreamId { get; } + public string EventType { get; } + public string TraceId { get; } + public string JustificationSummary { get; } + public string? EvidenceHash { get; } + public string? PayloadHash { get; } + public DateTimeOffset CreatedAt { get; } + public ImmutableDictionary Attributes { get; } + + private static string Ensure(string value, string name) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException($"{name} cannot be null or whitespace", name); + } + return value.Trim(); + } + + private static ImmutableDictionary Normalize(ImmutableDictionary? attributes) + { + if (attributes is null || attributes.Count == 0) + { + return ImmutableDictionary.Empty; + } + + var builder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + foreach (var kv in attributes) + { + if (string.IsNullOrWhiteSpace(kv.Key) || kv.Value is null) + { + continue; + } + builder[kv.Key.Trim()] = kv.Value; + } + return builder.ToImmutable(); + } +} diff --git a/src/Excititor/__Libraries/StellaOps.Excititor.Core/VexAttestationPayload.cs b/src/Excititor/__Libraries/StellaOps.Excititor.Core/VexAttestationPayload.cs new file mode 100644 index 000000000..319a8f4f1 --- /dev/null +++ b/src/Excititor/__Libraries/StellaOps.Excititor.Core/VexAttestationPayload.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace StellaOps.Excititor.Core; + +/// +/// Aggregation-only attestation payload describing evidence supplier identity and the observation/linkset it covers. +/// Used by Advisory AI / Policy to chain trust without Excititor interpreting verdicts. +/// +public sealed record VexAttestationPayload +{ + public VexAttestationPayload( + string attestationId, + string supplierId, + string observationId, + string linksetId, + string vulnerabilityId, + string productKey, + string? justificationSummary, + DateTimeOffset issuedAt, + ImmutableDictionary? metadata = null) + { + AttestationId = EnsureNotNullOrWhiteSpace(attestationId, nameof(attestationId)); + SupplierId = EnsureNotNullOrWhiteSpace(supplierId, nameof(supplierId)); + ObservationId = EnsureNotNullOrWhiteSpace(observationId, nameof(observationId)); + LinksetId = EnsureNotNullOrWhiteSpace(linksetId, nameof(linksetId)); + VulnerabilityId = EnsureNotNullOrWhiteSpace(vulnerabilityId, nameof(vulnerabilityId)); + ProductKey = EnsureNotNullOrWhiteSpace(productKey, nameof(productKey)); + JustificationSummary = TrimToNull(justificationSummary); + IssuedAt = issuedAt.ToUniversalTime(); + Metadata = NormalizeMetadata(metadata); + } + + public string AttestationId { get; } + public string SupplierId { get; } + public string ObservationId { get; } + public string LinksetId { get; } + public string VulnerabilityId { get; } + public string ProductKey { get; } + public string? JustificationSummary { get; } + public DateTimeOffset IssuedAt { get; } + public ImmutableDictionary Metadata { get; } + + private static ImmutableDictionary NormalizeMetadata(ImmutableDictionary? metadata) + { + if (metadata is null || metadata.Count == 0) + { + return ImmutableDictionary.Empty; + } + + var builder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + foreach (var pair in metadata.OrderBy(kv => kv.Key, StringComparer.Ordinal)) + { + var key = TrimToNull(pair.Key); + var value = TrimToNull(pair.Value); + if (key is null || value is null) + { + continue; + } + + builder[key] = value; + } + + return builder.ToImmutable(); + } + + private static string EnsureNotNullOrWhiteSpace(string value, string name) + => string.IsNullOrWhiteSpace(value) ? throw new ArgumentException($"{name} must be provided.", name) : value.Trim(); + + private static string? TrimToNull(string? value) + => string.IsNullOrWhiteSpace(value) ? null : value.Trim(); +} + +/// +/// Lightweight mapping from attestation IDs back to the observation/linkset/product tuple for provenance tracing. +/// +public sealed record VexAttestationLink +{ + public VexAttestationLink(string attestationId, string observationId, string linksetId, string productKey) + { + AttestationId = EnsureNotNullOrWhiteSpace(attestationId, nameof(attestationId)); + ObservationId = EnsureNotNullOrWhiteSpace(observationId, nameof(observationId)); + LinksetId = EnsureNotNullOrWhiteSpace(linksetId, nameof(linksetId)); + ProductKey = EnsureNotNullOrWhiteSpace(productKey, nameof(productKey)); + } + + public string AttestationId { get; } + + public string ObservationId { get; } + + public string LinksetId { get; } + + public string ProductKey { get; } + + private static string EnsureNotNullOrWhiteSpace(string value, string name) + => string.IsNullOrWhiteSpace(value) ? throw new ArgumentException($"{name} must be provided.", name) : value.Trim(); +} diff --git a/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/IVexAttestationLinkStore.cs b/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/IVexAttestationLinkStore.cs new file mode 100644 index 000000000..00a6888bc --- /dev/null +++ b/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/IVexAttestationLinkStore.cs @@ -0,0 +1,12 @@ +using System.Threading; +using System.Threading.Tasks; +using StellaOps.Excititor.Core; + +namespace StellaOps.Excititor.Storage.Mongo; + +public interface IVexAttestationLinkStore +{ + ValueTask UpsertAsync(VexAttestationPayload payload, CancellationToken cancellationToken); + + ValueTask FindAsync(string attestationId, CancellationToken cancellationToken); +} diff --git a/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/MongoVexAttestationLinkStore.cs b/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/MongoVexAttestationLinkStore.cs new file mode 100644 index 000000000..a5995415a --- /dev/null +++ b/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/MongoVexAttestationLinkStore.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver; +using StellaOps.Excititor.Core; + +namespace StellaOps.Excititor.Storage.Mongo; + +public sealed class MongoVexAttestationLinkStore : IVexAttestationLinkStore +{ + private readonly IMongoCollection _collection; + + public MongoVexAttestationLinkStore(IMongoDatabase database) + { + ArgumentNullException.ThrowIfNull(database); + VexMongoMappingRegistry.Register(); + _collection = database.GetCollection(VexMongoCollectionNames.Attestations); + } + + public async ValueTask UpsertAsync(VexAttestationPayload payload, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(payload); + + var record = VexAttestationLinkRecord.FromDomain(payload); + var filter = Builders.Filter.Eq(x => x.AttestationId, record.AttestationId); + var options = new ReplaceOptions { IsUpsert = true }; + + await _collection.ReplaceOneAsync(filter, record, options, cancellationToken).ConfigureAwait(false); + } + + public async ValueTask FindAsync(string attestationId, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(attestationId)) + { + throw new ArgumentException("Attestation id must be provided.", nameof(attestationId)); + } + + var filter = Builders.Filter.Eq(x => x.AttestationId, attestationId.Trim()); + var record = await _collection.Find(filter).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + return record?.ToDomain(); + } +} diff --git a/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/VexAttestationLinkRecord.cs b/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/VexAttestationLinkRecord.cs new file mode 100644 index 000000000..ad37a2837 --- /dev/null +++ b/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/VexAttestationLinkRecord.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using MongoDB.Bson.Serialization.Attributes; +using StellaOps.Excititor.Core; + +namespace StellaOps.Excititor.Storage.Mongo; + +[BsonIgnoreExtraElements] +internal sealed class VexAttestationLinkRecord +{ + [BsonId] + public string AttestationId { get; set; } = default!; + + public string SupplierId { get; set; } = default!; + + public string ObservationId { get; set; } = default!; + + public string LinksetId { get; set; } = default!; + + public string VulnerabilityId { get; set; } = default!; + + public string ProductKey { get; set; } = default!; + + public string? JustificationSummary { get; set; } + = null; + + public DateTime IssuedAt { get; set; } + = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc); + + public Dictionary Metadata { get; set; } = new(StringComparer.Ordinal); + + public static VexAttestationLinkRecord FromDomain(VexAttestationPayload payload) + => new() + { + AttestationId = payload.AttestationId, + SupplierId = payload.SupplierId, + ObservationId = payload.ObservationId, + LinksetId = payload.LinksetId, + VulnerabilityId = payload.VulnerabilityId, + ProductKey = payload.ProductKey, + JustificationSummary = payload.JustificationSummary, + IssuedAt = payload.IssuedAt.UtcDateTime, + Metadata = payload.Metadata.ToDictionary(kv => kv.Key, kv => kv.Value, StringComparer.Ordinal), + }; + + public VexAttestationPayload ToDomain() + { + var metadata = (Metadata ?? new Dictionary(StringComparer.Ordinal)) + .ToImmutableDictionary(StringComparer.Ordinal); + + return new VexAttestationPayload( + AttestationId, + SupplierId, + ObservationId, + LinksetId, + VulnerabilityId, + ProductKey, + JustificationSummary, + new DateTimeOffset(DateTime.SpecifyKind(IssuedAt, DateTimeKind.Utc)), + metadata); + } +} diff --git a/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/VexMongoMappingRegistry.cs b/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/VexMongoMappingRegistry.cs index ec085b932..d911aea48 100644 --- a/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/VexMongoMappingRegistry.cs +++ b/src/Excititor/__Libraries/StellaOps.Excititor.Storage.Mongo/VexMongoMappingRegistry.cs @@ -70,10 +70,11 @@ public static class VexMongoCollectionNames public const string Providers = "vex.providers"; public const string Raw = "vex.raw"; public const string Statements = "vex.statements"; - public const string Claims = Statements; - public const string Consensus = "vex.consensus"; + public const string Claims = Statements; + public const string Consensus = "vex.consensus"; public const string Exports = "vex.exports"; public const string Cache = "vex.cache"; public const string ConnectorState = "vex.connector_state"; public const string ConsensusHolds = "vex.consensus_holds"; + public const string Attestations = "vex.attestations"; } diff --git a/src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/Observations/TimelineEventTests.cs b/src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/Observations/TimelineEventTests.cs new file mode 100644 index 000000000..579084df1 --- /dev/null +++ b/src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/Observations/TimelineEventTests.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Immutable; +using FluentAssertions; +using StellaOps.Excititor.Core.Observations; +using Xunit; + +namespace StellaOps.Excititor.Core.Tests.Observations; + +public class TimelineEventTests +{ + [Fact] + public void Normalizes_and_requires_fields() + { + var evt = new TimelineEvent( + eventId: " EVT-1 ", + tenant: "TenantA", + providerId: "prov", + streamId: "stream", + eventType: "ingest", + traceId: "trace-123", + justificationSummary: " summary ", + createdAt: DateTimeOffset.UnixEpoch, + evidenceHash: " evhash ", + payloadHash: " pwhash ", + attributes: ImmutableDictionary.Empty.Add(" a ", " b " )); + + evt.EventId.Should().Be("EVT-1"); + evt.Tenant.Should().Be("tenanta"); + evt.JustificationSummary.Should().Be("summary"); + evt.EvidenceHash.Should().Be("evhash"); + evt.PayloadHash.Should().Be("pwhash"); + evt.Attributes.Should().ContainKey("a"); + } + + [Fact] + public void Throws_on_missing_required() + { + Action act = () => new TimelineEvent(" ", "t", "p", "s", "t", "trace", "", DateTimeOffset.UtcNow); + act.Should().Throw(); + } +} diff --git a/src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/VexAttestationPayloadTests.cs b/src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/VexAttestationPayloadTests.cs new file mode 100644 index 000000000..218a72007 --- /dev/null +++ b/src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/VexAttestationPayloadTests.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Immutable; +using FluentAssertions; +using StellaOps.Excititor.Core; +using Xunit; + +namespace StellaOps.Excititor.Core.Tests; + +public sealed class VexAttestationPayloadTests +{ + [Fact] + public void Payload_NormalizesAndOrdersMetadata() + { + var metadata = ImmutableDictionary.Empty + .Add(b, diff --git a/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexAttestationLinkEndpointTests.cs b/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexAttestationLinkEndpointTests.cs new file mode 100644 index 000000000..5c8f91486 --- /dev/null +++ b/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexAttestationLinkEndpointTests.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using EphemeralMongo; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.Configuration; +using StellaOps.Excititor.Core; +using StellaOps.Excititor.Storage.Mongo; +using Xunit; + +namespace StellaOps.Excititor.WebService.Tests; + +public sealed class VexAttestationLinkEndpointTests : IDisposable +{ + private readonly IMongoRunner _runner; + private readonly TestWebApplicationFactory _factory; + + public VexAttestationLinkEndpointTests() + { + _runner = MongoRunner.Run(new MongoRunnerOptions { UseSingleNodeReplicaSet = true }); + + _factory = new TestWebApplicationFactory( + configureConfiguration: configuration => + { + configuration.AddInMemoryCollection(new Dictionary + { + [Excititor:Storage:Mongo:ConnectionString] = _runner.ConnectionString, + [Excititor:Storage:Mongo:DatabaseName] = vex_attestation_links, + [Excititor:Storage:Mongo:DefaultTenant] = tests, + }); + }, + configureServices: services => + { + TestServiceOverrides.Apply(services); + services.AddTestAuthentication(); + }); + + SeedLink(); + } + + [Fact] + public async Task GetAttestationLink_ReturnsPayload() + { + using var client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Bearer, vex.read); + + var response = await client.GetAsync(/v1/vex/attestations/att-123); + response.EnsureSuccessStatusCode(); + + var payload = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(payload); + Assert.Equal(att-123, payload!.AttestationId); + Assert.Equal(supplier-a, payload.SupplierId); + Assert.Equal(CVE-2025-0001, payload.VulnerabilityId); + Assert.Equal(pkg:demo, payload.ProductKey); + } + + private void SeedLink() + { + var client = new MongoDB.Driver.MongoClient(_runner.ConnectionString); + var database = client.GetDatabase(vex_attestation_links); + var collection = database.GetCollection(VexMongoCollectionNames.Attestations); + + var record = new VexAttestationLinkRecord + { + AttestationId = att-123, + SupplierId = supplier-a, + ObservationId = obs-1, + LinksetId = link-1, + VulnerabilityId = CVE-2025-0001, + ProductKey = pkg:demo, + JustificationSummary = summary, + IssuedAt = DateTime.UtcNow, + Metadata = new Dictionary { [policyRevisionId] = rev-1 }, + }; + + collection.InsertOne(record); + } + + public void Dispose() + { + _factory.Dispose(); + _runner.Dispose(); + } +} diff --git a/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexEvidenceChunkServiceTests.cs b/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexEvidenceChunkServiceTests.cs new file mode 100644 index 000000000..e751540de --- /dev/null +++ b/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexEvidenceChunkServiceTests.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using StellaOps.Excititor.Core; +using StellaOps.Excititor.Storage.Mongo; +using StellaOps.Excititor.WebService.Services; +using Xunit; + +namespace StellaOps.Excititor.WebService.Tests; + +public sealed class VexEvidenceChunkServiceTests +{ + [Fact] + public async Task QueryAsync_FiltersAndLimitsResults() + { + var now = new DateTimeOffset(2025, 11, 16, 12, 0, 0, TimeSpan.Zero); + var claims = new[] + { + CreateClaim("provider-a", VexClaimStatus.Affected, now.AddHours(-6), now.AddHours(-5), score: 0.9), + CreateClaim("provider-b", VexClaimStatus.NotAffected, now.AddHours(-4), now.AddHours(-3), score: 0.2) + }; + + var service = new VexEvidenceChunkService(new FakeClaimStore(claims), new FixedTimeProvider(now)); + var request = new VexEvidenceChunkRequest( + Tenant: "tenant-a", + VulnerabilityId: "CVE-2025-0001", + ProductKey: "pkg:docker/demo", + ProviderIds: ImmutableHashSet.Create("provider-b"), + Statuses: ImmutableHashSet.Create(VexClaimStatus.NotAffected), + Since: null, + Limit: 1); + + var result = await service.QueryAsync(request, CancellationToken.None); + + result.Truncated.Should().BeTrue(); + result.TotalCount.Should().Be(1); + result.GeneratedAtUtc.Should().Be(now); + var chunk = result.Chunks.Single(); + chunk.ProviderId.Should().Be("provider-b"); + chunk.Status.Should().Be(VexClaimStatus.NotAffected.ToString()); + chunk.ScopeScore.Should().Be(0.2); + chunk.ObservationId.Should().Contain("provider-b"); + chunk.Document.Digest.Should().NotBeNullOrWhiteSpace(); + } + + private static VexClaim CreateClaim(string providerId, VexClaimStatus status, DateTimeOffset firstSeen, DateTimeOffset lastSeen, double? score) + { + var product = new VexProduct("pkg:docker/demo", "demo", "1.0.0", "pkg:docker/demo:1.0.0", null, new[] { "component-a" }); + var document = new VexClaimDocument( + VexDocumentFormat.SbomCycloneDx, + digest: Guid.NewGuid().ToString("N"), + sourceUri: new Uri("https://example.test/vex.json"), + revision: "r1", + signature: new VexSignatureMetadata("cosign", "demo", "issuer", keyId: "kid", verifiedAt: firstSeen, transparencyLogReference: null)); + + var signals = score.HasValue + ? new VexSignalSnapshot(new VexSeveritySignal("cvss", score, "low", vector: null), Kev: null, Epss: null) + : null; + + return new VexClaim( + "CVE-2025-0001", + providerId, + product, + status, + document, + firstSeen, + lastSeen, + justification: VexJustification.ComponentNotPresent, + detail: "demo detail", + confidence: null, + signals: signals, + additionalMetadata: ImmutableDictionary.Empty); + } + + private sealed class FakeClaimStore : IVexClaimStore + { + private readonly IReadOnlyCollection _claims; + + public FakeClaimStore(IReadOnlyCollection claims) + { + _claims = claims; + } + + public ValueTask AppendAsync(IEnumerable claims, DateTimeOffset observedAt, CancellationToken cancellationToken, MongoDB.Driver.IClientSessionHandle? session = null) + => throw new NotSupportedException(); + + public ValueTask> FindAsync(string vulnerabilityId, string productKey, DateTimeOffset? since, CancellationToken cancellationToken, MongoDB.Driver.IClientSessionHandle? session = null) + { + var query = _claims + .Where(claim => claim.VulnerabilityId == vulnerabilityId) + .Where(claim => claim.Product.Key == productKey); + + if (since.HasValue) + { + query = query.Where(claim => claim.LastSeen >= since.Value); + } + + return ValueTask.FromResult>(query.ToList()); + } + } + + private sealed class FixedTimeProvider : TimeProvider + { + private readonly DateTimeOffset _timestamp; + + public FixedTimeProvider(DateTimeOffset timestamp) + { + _timestamp = timestamp; + } + + public override DateTimeOffset GetUtcNow() => _timestamp; + } +} diff --git a/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexEvidenceChunksEndpointTests.cs b/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexEvidenceChunksEndpointTests.cs new file mode 100644 index 000000000..405e6031b --- /dev/null +++ b/src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/VexEvidenceChunksEndpointTests.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading.Tasks; +using EphemeralMongo; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Driver; +using StellaOps.Excititor.Core; +using StellaOps.Excititor.Storage.Mongo; +using StellaOps.Excititor.WebService.Contracts; +using Xunit; + +namespace StellaOps.Excititor.WebService.Tests; + +public sealed class VexEvidenceChunksEndpointTests : IDisposable +{ + private readonly IMongoRunner _runner; + private readonly TestWebApplicationFactory _factory; + + public VexEvidenceChunksEndpointTests() + { + _runner = MongoRunner.Run(new MongoRunnerOptions { UseSingleNodeReplicaSet = true }); + + _factory = new TestWebApplicationFactory( + configureConfiguration: configuration => + { + configuration.AddInMemoryCollection(new Dictionary + { + ["Excititor:Storage:Mongo:ConnectionString"] = _runner.ConnectionString, + ["Excititor:Storage:Mongo:DatabaseName"] = "vex_chunks_tests", + ["Excititor:Storage:Mongo:DefaultTenant"] = "tests", + }); + }, + configureServices: services => + { + TestServiceOverrides.Apply(services); + services.AddTestAuthentication(); + }); + + SeedStatements(); + } + + [Fact] + public async Task ChunksEndpoint_Filters_ByProvider_AndStreamsNdjson() + { + using var client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false }); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "vex.read"); + client.DefaultRequestHeaders.Add("X-Stella-Tenant", "tests"); + + var response = await client.GetAsync("/v1/vex/evidence/chunks?vulnerabilityId=CVE-2025-0001&productKey=pkg:docker/demo&providerId=provider-b&limit=1"); + response.EnsureSuccessStatusCode(); + + Assert.True(response.Headers.TryGetValues("Excititor-Results-Truncated", out var truncatedValues)); + Assert.Contains("true", truncatedValues, StringComparer.OrdinalIgnoreCase); + + var body = await response.Content.ReadAsStringAsync(); + var lines = body.Split(n, StringSplitOptions.RemoveEmptyEntries); + Assert.Single(lines); + + var chunk = JsonSerializer.Deserialize(lines[0], new JsonSerializerOptions(JsonSerializerDefaults.Web)); + Assert.NotNull(chunk); + Assert.Equal("provider-b", chunk!.ProviderId); + Assert.Equal("NotAffected", chunk.Status); + Assert.Equal("pkg:docker/demo", chunk.Scope.Key); + Assert.Equal("CVE-2025-0001", chunk.VulnerabilityId); + } + + private void SeedStatements() + { + var client = new MongoClient(_runner.ConnectionString); + var database = client.GetDatabase("vex_chunks_tests"); + var collection = database.GetCollection(VexMongoCollectionNames.Statements); + + var now = DateTimeOffset.UtcNow; + var claims = new[] + { + CreateClaim("provider-a", VexClaimStatus.Affected, now.AddHours(-6), now.AddHours(-5), 0.9), + CreateClaim("provider-b", VexClaimStatus.NotAffected, now.AddHours(-4), now.AddHours(-3), 0.2), + CreateClaim("provider-c", VexClaimStatus.Affected, now.AddHours(-2), now.AddHours(-1), 0.5) + }; + + var records = claims + .Select(claim => VexStatementRecord.FromDomain(claim, now)) + .ToList(); + + collection.InsertMany(records); + } + + private static VexClaim CreateClaim(string providerId, VexClaimStatus status, DateTimeOffset firstSeen, DateTimeOffset lastSeen, double? score) + { + var product = new VexProduct("pkg:docker/demo", "demo", "1.0.0", "pkg:docker/demo:1.0.0", null, new[] { "component-a" }); + var document = new VexClaimDocument( + VexDocumentFormat.SbomCycloneDx, + digest: Guid.NewGuid().ToString("N"), + sourceUri: new Uri("https://example.test/vex.json"), + revision: "r1", + signature: new VexSignatureMetadata("cosign", "demo", "issuer", keyId: "kid", verifiedAt: firstSeen, transparencyLogReference: null)); + + var signals = score.HasValue + ? new VexSignalSnapshot(new VexSeveritySignal("cvss", score, "low", vector: null), Kev: null, Epss: null) + : null; + + return new VexClaim( + "CVE-2025-0001", + providerId, + product, + status, + document, + firstSeen, + lastSeen, + justification: VexJustification.ComponentNotPresent, + detail: "demo detail", + confidence: null, + signals: signals, + additionalMetadata: null); + } + + public void Dispose() + { + _factory.Dispose(); + _runner.Dispose(); + } +} diff --git a/src/Findings/StellaOps.Findings.Ledger/Infrastructure/Compatibility/IsExternalInit.cs b/src/Findings/StellaOps.Findings.Ledger/Infrastructure/Compatibility/IsExternalInit.cs new file mode 100644 index 000000000..7b7e61411 --- /dev/null +++ b/src/Findings/StellaOps.Findings.Ledger/Infrastructure/Compatibility/IsExternalInit.cs @@ -0,0 +1,7 @@ +// Temporary shim for compilers that do not surface System.Runtime.CompilerServices.IsExternalInit +// (needed for record types). Remove when toolchain natively provides the type. +namespace System.Runtime.CompilerServices; + +internal static class IsExternalInit +{ +} diff --git a/src/Findings/StellaOps.Findings.Ledger/fixtures/sample-small.ndjson b/src/Findings/StellaOps.Findings.Ledger/fixtures/sample-small.ndjson new file mode 100644 index 000000000..4d5c25ddb --- /dev/null +++ b/src/Findings/StellaOps.Findings.Ledger/fixtures/sample-small.ndjson @@ -0,0 +1,2 @@ +{"tenant": "tenant-a", "chain_id": "c8d6f7f1-58f8-4c2d-8d92-f9b8790a0001", "sequence_no": 1, "event_id": "c0e6d9b4-1d89-4b07-b622-1c7b6d111001", "event_type": "finding.assignment", "policy_version": "2025.01", "finding_id": "F-001", "artifact_id": "artifact-1", "actor_id": "system", "actor_type": "system", "occurred_at": "2025-01-01T00:00:00Z", "recorded_at": "2025-01-01T00:00:01Z", "payload": {"comment": "seed event"}, "previous_hash": "0000000000000000000000000000000000000000000000000000000000000000", "event_hash": "0d95f63532b6488407e8fd2e837edb3e9bfc8a2defde232aca99dbfd518558c6", "merkle_leaf_hash": "d08d4da76da50fbe4274a394c73fcaae0180fd591238d224bc7d5efee2ad3696"} +{"tenant": "tenant-a", "chain_id": "c8d6f7f1-58f8-4c2d-8d92-f9b8790a0001", "sequence_no": 2, "event_id": "c0e6d9b4-1d89-4b07-b622-1c7b6d111002", "event_type": "finding.comment", "policy_version": "2025.01", "finding_id": "F-001", "artifact_id": "artifact-1", "actor_id": "analyst", "actor_type": "operator", "occurred_at": "2025-01-01T00:00:10Z", "recorded_at": "2025-01-01T00:00:11Z", "payload": {"comment": "follow-up"}, "previous_hash": "PLACEHOLDER", "event_hash": "0e77979af948be38de028a2497f15529473ae5aeb0a95f5d9d648efc8afb9fa3", "merkle_leaf_hash": "2854050efba048f2674ba27fd7dc2f1b65e90e150098bfeeb4fc6e23334c3790"} diff --git a/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/.placeholder b/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/.placeholder new file mode 100644 index 000000000..e69de29bb diff --git a/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/LedgerReplayHarness.csproj b/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/LedgerReplayHarness.csproj new file mode 100644 index 000000000..1bf1b24aa --- /dev/null +++ b/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/LedgerReplayHarness.csproj @@ -0,0 +1,15 @@ + + + Exe + net10.0 + preview + enable + enable + + + + + + + + diff --git a/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/Program.cs b/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/Program.cs new file mode 100644 index 000000000..2cc13d9c8 --- /dev/null +++ b/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/Program.cs @@ -0,0 +1,502 @@ +using System.CommandLine; +using System.Diagnostics; +using System.Diagnostics.Metrics; +using System.Text.Json; +using System.Text.Json.Nodes; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using StellaOps.Findings.Ledger.Domain; +using StellaOps.Findings.Ledger.Hashing; +using StellaOps.Findings.Ledger.Infrastructure; +using StellaOps.Findings.Ledger.Infrastructure.Merkle; +using StellaOps.Findings.Ledger.Infrastructure.Postgres; +using StellaOps.Findings.Ledger.Infrastructure.Projection; +using StellaOps.Findings.Ledger.Options; +using StellaOps.Findings.Ledger.Observability; +using StellaOps.Findings.Ledger.Services; + +// Command-line options +var fixturesOption = new Option( + name: "--fixture", + description: "NDJSON fixtures containing canonical ledger envelopes (sequence-ordered)") +{ + IsRequired = true +}; +fixturesOption.AllowMultipleArgumentsPerToken = true; + +var connectionOption = new Option( + name: "--connection", + description: "PostgreSQL connection string for ledger DB") +{ + IsRequired = true +}; + +var tenantOption = new Option( + name: "--tenant", + getDefaultValue: () => "tenant-a", + description: "Tenant identifier for appended events"); + +var maxParallelOption = new Option( + name: "--maxParallel", + getDefaultValue: () => 4, + description: "Maximum concurrent append operations"); + +var reportOption = new Option( + name: "--report", + description: "Path to write harness report JSON (with DSSE placeholder)"); + +var metricsOption = new Option( + name: "--metrics", + description: "Optional path to write metrics snapshot JSON"); + +var root = new RootCommand("Findings Ledger Replay Harness (LEDGER-29-008)"); +root.AddOption(fixturesOption); +root.AddOption(connectionOption); +root.AddOption(tenantOption); +root.AddOption(maxParallelOption); +root.AddOption(reportOption); +root.AddOption(metricsOption); + +root.SetHandler(async (FileInfo[] fixtures, string connection, string tenant, int maxParallel, FileInfo? reportFile, FileInfo? metricsFile) => +{ + await using var host = BuildHost(connection); + using var scope = host.Services.CreateScope(); + + var writeService = scope.ServiceProvider.GetRequiredService(); + var projectionWorker = scope.ServiceProvider.GetRequiredService(); + var anchorWorker = scope.ServiceProvider.GetRequiredService(); + var logger = scope.ServiceProvider.GetRequiredService().CreateLogger("Harness"); + var timeProvider = scope.ServiceProvider.GetRequiredService(); + + var cts = new CancellationTokenSource(); + var projectionTask = projectionWorker.StartAsync(cts.Token); + var anchorTask = anchorWorker.StartAsync(cts.Token); + + var (meterListener, metrics) = CreateMeterListener(); + + var sw = Stopwatch.StartNew(); + long eventsWritten = 0; + + await Parallel.ForEachAsync(fixtures, new ParallelOptions { MaxDegreeOfParallelism = maxParallel, CancellationToken = cts.Token }, async (file, token) => + { + await foreach (var draft in ReadDraftsAsync(file, tenant, timeProvider, token)) + { + var result = await writeService.AppendAsync(draft, token).ConfigureAwait(false); + if (result.Status is LedgerWriteStatus.ValidationFailed or LedgerWriteStatus.Conflict) + { + throw new InvalidOperationException($"Append failed for {draft.EventId}: {string.Join(",", result.Errors)} ({result.ConflictCode})"); + } + + Interlocked.Increment(ref eventsWritten); + if (eventsWritten % 50_000 == 0) + { + logger.LogInformation("Appended {Count} events...", eventsWritten); + } + } + }).ConfigureAwait(false); + + // Wait for projector to catch up + await Task.Delay(TimeSpan.FromSeconds(2), cts.Token); + sw.Stop(); + + meterListener.RecordObservableInstruments(); + + var verification = await VerifyLedgerAsync(scope.ServiceProvider, tenant, eventsWritten, cts.Token).ConfigureAwait(false); + + var writeLatencyP95Ms = Percentile(metrics.HistDouble("ledger_write_latency_seconds"), 95) * 1000; + var rebuildP95Ms = Percentile(metrics.HistDouble("ledger_projection_rebuild_seconds"), 95) * 1000; + var projectionLagSeconds = metrics.GaugeDouble("ledger_projection_lag_seconds").DefaultIfEmpty(0).Max(); + var backlogEvents = metrics.GaugeLong("ledger_ingest_backlog_events").DefaultIfEmpty(0).Max(); + var dbConnections = metrics.GaugeLong("ledger_db_connections_active").DefaultIfEmpty(0).Sum(); + + var report = new HarnessReport( + tenant, + fixtures.Select(f => f.FullName).ToArray(), + eventsWritten, + sw.Elapsed.TotalSeconds, + status: verification.Success ? "pass" : "fail", + WriteLatencyP95Ms: writeLatencyP95Ms, + ProjectionRebuildP95Ms: rebuildP95Ms, + ProjectionLagSecondsMax: projectionLagSeconds, + BacklogEventsMax: backlogEvents, + DbConnectionsObserved: dbConnections, + VerificationErrors: verification.Errors.ToArray()); + + var jsonOptions = new JsonSerializerOptions { WriteIndented = true }; + var json = JsonSerializer.Serialize(report, jsonOptions); + Console.WriteLine(json); + + if (reportFile is not null) + { + await File.WriteAllTextAsync(reportFile.FullName, json, cts.Token).ConfigureAwait(false); + await WriteDssePlaceholderAsync(reportFile.FullName, json, cts.Token).ConfigureAwait(false); + } + + if (metricsFile is not null) + { + var snapshot = metrics.ToSnapshot(); + var metricsJson = JsonSerializer.Serialize(snapshot, jsonOptions); + await File.WriteAllTextAsync(metricsFile.FullName, metricsJson, cts.Token).ConfigureAwait(false); + } + + cts.Cancel(); + await Task.WhenAll(projectionTask, anchorTask).WaitAsync(TimeSpan.FromSeconds(5)); +}, fixturesOption, connectionOption, tenantOption, maxParallelOption, reportOption, metricsOption); + +await root.InvokeAsync(args); + +static async Task WriteDssePlaceholderAsync(string reportPath, string json, CancellationToken cancellationToken) +{ + using var sha = System.Security.Cryptography.SHA256.Create(); + var digest = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(json)); + var sig = new + { + payloadType = "application/vnd.stella-ledger-harness+json", + sha256 = Convert.ToHexString(digest).ToLowerInvariant(), + signedBy = "harness-local", + createdAt = DateTimeOffset.UtcNow + }; + + var sigJson = JsonSerializer.Serialize(sig, new JsonSerializerOptions { WriteIndented = true }); + await File.WriteAllTextAsync(reportPath + ".sig", sigJson, cancellationToken).ConfigureAwait(false); +} + +static (MeterListener Listener, MetricsBag Bag) CreateMeterListener() +{ + var bag = new MetricsBag(); + var listener = new MeterListener + { + InstrumentPublished = (instrument, meterListener) => + { + if (instrument.Meter.Name == "StellaOps.Findings.Ledger") + { + meterListener.EnableMeasurementEvents(instrument); + } + } + }; + + listener.SetMeasurementEventCallback((instrument, measurement, tags, _) => + { + bag.Add(instrument, measurement, tags); + }); + listener.SetMeasurementEventCallback((instrument, measurement, tags, _) => + { + bag.Add(instrument, measurement, tags); + }); + + listener.Start(); + return (listener, bag); +} + +static IHost BuildHost(string connectionString) +{ + return Host.CreateDefaultBuilder() + .ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.AddSimpleConsole(options => + { + options.SingleLine = true; + options.TimestampFormat = "HH:mm:ss "; + }); + }) + .ConfigureServices(services => + { + services.Configure(opts => + { + opts.Database.ConnectionString = connectionString; + }); + + services.AddSingleton(_ => TimeProvider.System); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + }) + .Build(); +} + +static async IAsyncEnumerable ReadDraftsAsync(FileInfo file, string tenant, TimeProvider timeProvider, [EnumeratorCancellation] CancellationToken cancellationToken) +{ + await using var stream = file.OpenRead(); + using var reader = new StreamReader(stream); + var recordedAtBase = timeProvider.GetUtcNow(); + + while (!reader.EndOfStream) + { + var line = await reader.ReadLineAsync().ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var node = JsonNode.Parse(line)?.AsObject(); + if (node is null) + { + continue; + } + + yield return ToDraft(node, tenant, recordedAtBase); + cancellationToken.ThrowIfCancellationRequested(); + } +} + +static LedgerEventDraft ToDraft(JsonObject node, string defaultTenant, DateTimeOffset recordedAtBase) +{ + string required(string name) => node[name]?.GetValue() ?? throw new InvalidOperationException($"{name} missing"); + + var tenantId = node.TryGetPropertyValue("tenant", out var tenantNode) + ? tenantNode!.GetValue() + : defaultTenant; + + var chainId = Guid.Parse(required("chain_id")); + var sequence = node["sequence_no"]?.GetValue() ?? node["sequence"]?.GetValue() ?? throw new InvalidOperationException("sequence_no missing"); + var eventId = Guid.Parse(required("event_id")); + var eventType = required("event_type"); + var policyVersion = required("policy_version"); + var findingId = required("finding_id"); + var artifactId = required("artifact_id"); + var sourceRunId = node.TryGetPropertyValue("source_run_id", out var sourceRunNode) && sourceRunNode is not null && !string.IsNullOrWhiteSpace(sourceRunNode.GetValue()) + ? Guid.Parse(sourceRunNode!.GetValue()) + : null; + var actorId = required("actor_id"); + var actorType = required("actor_type"); + var occurredAt = DateTimeOffset.Parse(required("occurred_at")); + var recordedAt = node.TryGetPropertyValue("recorded_at", out var recordedAtNode) && recordedAtNode is not null + ? DateTimeOffset.Parse(recordedAtNode.GetValue()) + : recordedAtBase; + + var payload = node.TryGetPropertyValue("payload", out var payloadNode) && payloadNode is JsonObject payloadObj + ? payloadObj + : throw new InvalidOperationException("payload missing"); + + var canonicalEnvelope = LedgerCanonicalJsonSerializer.Canonicalize(payload); + var prev = node.TryGetPropertyValue("previous_hash", out var prevNode) ? prevNode?.GetValue() : null; + + return new LedgerEventDraft( + tenantId, + chainId, + sequence, + eventId, + eventType, + policyVersion, + findingId, + artifactId, + sourceRunId, + actorId, + actorType, + occurredAt, + recordedAt, + payload, + canonicalEnvelope, + prev); +} + +static async Task VerifyLedgerAsync(IServiceProvider services, string tenant, long expectedEvents, CancellationToken cancellationToken) +{ + var errors = new List(); + var dataSource = services.GetRequiredService(); + + await using var connection = await dataSource.OpenConnectionAsync(tenant, "verify", cancellationToken).ConfigureAwait(false); + + // Count check + await using (var countCommand = new Npgsql.NpgsqlCommand("select count(*) from ledger_events where tenant_id = @tenant", connection)) + { + countCommand.Parameters.AddWithValue("tenant", tenant); + var count = (long)await countCommand.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); + if (count < expectedEvents) + { + errors.Add($"event_count_mismatch:{count}/{expectedEvents}"); + } + } + + // Sequence and hash verification + const string query = """ + select chain_id, sequence_no, event_id, event_body, event_hash, previous_hash, merkle_leaf_hash + from ledger_events + where tenant_id = @tenant + order by chain_id, sequence_no + """; + + await using var command = new Npgsql.NpgsqlCommand(query, connection); + command.Parameters.AddWithValue("tenant", tenant); + + await using var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); + + Guid? currentChain = null; + long expectedSequence = 1; + string? prevHash = null; + + while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) + { + var chainId = reader.GetGuid(0); + var sequence = reader.GetInt64(1); + var eventId = reader.GetGuid(2); + var eventBodyJson = reader.GetString(3); + var eventHash = reader.GetString(4); + var previousHash = reader.GetString(5); + var merkleLeafHash = reader.GetString(6); + + if (currentChain != chainId) + { + currentChain = chainId; + expectedSequence = 1; + prevHash = LedgerEventConstants.EmptyHash; + } + + if (sequence != expectedSequence) + { + errors.Add($"sequence_gap:{chainId}:{sequence}"); + } + + if (!string.Equals(previousHash, prevHash, StringComparison.Ordinal)) + { + errors.Add($"previous_hash_mismatch:{chainId}:{sequence}"); + } + + var node = JsonNode.Parse(eventBodyJson)?.AsObject() ?? new JsonObject(); + var canonical = LedgerCanonicalJsonSerializer.Canonicalize(node); + var hashResult = LedgerHashing.ComputeHashes(canonical, sequence); + + if (!string.Equals(hashResult.EventHash, eventHash, StringComparison.Ordinal)) + { + errors.Add($"event_hash_mismatch:{eventId}"); + } + + if (!string.Equals(hashResult.MerkleLeafHash, merkleLeafHash, StringComparison.Ordinal)) + { + errors.Add($"merkle_leaf_mismatch:{eventId}"); + } + + prevHash = eventHash; + expectedSequence++; + } + + if (errors.Count == 0) + { + // Additional check: projector caught up (no lag > 0) + var lagMax = LedgerMetricsSnapshot.LagMax; + if (lagMax > 0) + { + errors.Add($"projection_lag_remaining:{lagMax}"); + } + } + + return new VerificationResult(errors.Count == 0, errors); +} + +static double Percentile(IEnumerable values, double percentile) +{ + var data = values.Where(v => !double.IsNaN(v)).OrderBy(v => v).ToArray(); + if (data.Length == 0) + { + return 0; + } + + var rank = (percentile / 100.0) * (data.Length - 1); + var lowerIndex = (int)Math.Floor(rank); + var upperIndex = (int)Math.Ceiling(rank); + if (lowerIndex == upperIndex) + { + return data[lowerIndex]; + } + + var fraction = rank - lowerIndex; + return data[lowerIndex] + (data[upperIndex] - data[lowerIndex]) * fraction; +} + +internal sealed record HarnessReport( + string Tenant, + IReadOnlyList Fixtures, + long EventsWritten, + double DurationSeconds, + string Status, + double WriteLatencyP95Ms, + double ProjectionRebuildP95Ms, + double ProjectionLagSecondsMax, + double BacklogEventsMax, + long DbConnectionsObserved, + IReadOnlyList VerificationErrors); + +internal sealed record VerificationResult(bool Success, IReadOnlyList Errors); + +internal sealed class MetricsBag +{ + private readonly List<(string Name, double Value)> doubles = new(); + private readonly List<(string Name, long Value)> longs = new(); + + public void Add(Instrument instrument, double value, ReadOnlySpan> _) + => doubles.Add((instrument.Name, value)); + + public void Add(Instrument instrument, long value, ReadOnlySpan> _) + => longs.Add((instrument.Name, value)); + + public IEnumerable HistDouble(string name) => doubles.Where(d => d.Name == name).Select(d => d.Value); + public IEnumerable GaugeDouble(string name) => doubles.Where(d => d.Name == name).Select(d => d.Value); + public IEnumerable GaugeLong(string name) => longs.Where(l => l.Name == name).Select(l => l.Value); + + public object ToSnapshot() => new + { + doubles = doubles.GroupBy(x => x.Name).ToDictionary(g => g.Key, g => g.Select(v => v.Value).ToArray()), + longs = longs.GroupBy(x => x.Name).ToDictionary(g => g.Key, g => g.Select(v => v.Value).ToArray()) + }; +} + +// Harness lightweight no-op implementations for projection/merkle to keep replay fast +internal sealed class NoOpPolicyEvaluationService : IPolicyEvaluationService +{ + public Task EvaluateAsync(LedgerEventRecord record, FindingProjection? current, CancellationToken cancellationToken) + { + return Task.FromResult(new PolicyEvaluationResult("noop", record.OccurredAt, record.RecordedAt, current?.Status ?? "new")); + } +} + +internal sealed class NoOpProjectionRepository : IFindingProjectionRepository +{ + public Task GetAsync(string tenantId, string findingId, string policyVersion, CancellationToken cancellationToken) => + Task.FromResult(null); + + public Task InsertActionAsync(FindingAction action, CancellationToken cancellationToken) => Task.CompletedTask; + + public Task InsertHistoryAsync(FindingHistory history, CancellationToken cancellationToken) => Task.CompletedTask; + + public Task SaveCheckpointAsync(ProjectionCheckpoint checkpoint, CancellationToken cancellationToken) => Task.CompletedTask; + + public Task GetCheckpointAsync(CancellationToken cancellationToken) => + Task.FromResult(new ProjectionCheckpoint(DateTimeOffset.MinValue, Guid.Empty, DateTimeOffset.MinValue)); + + public Task UpsertAsync(FindingProjection projection, CancellationToken cancellationToken) => Task.CompletedTask; + + public Task EnsureIndexesAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} + +internal sealed class NoOpMerkleAnchorRepository : IMerkleAnchorRepository +{ + public Task InsertAsync(string tenantId, Guid anchorId, DateTimeOffset windowStart, DateTimeOffset windowEnd, long sequenceStart, long sequenceEnd, string rootHash, long leafCount, DateTime anchoredAt, string? anchorReference, CancellationToken cancellationToken) + => Task.CompletedTask; + + public Task GetLatestAsync(string tenantId, CancellationToken cancellationToken) => + Task.FromResult(null); +} + +internal sealed class QueueMerkleAnchorScheduler : IMerkleAnchorScheduler +{ + private readonly LedgerAnchorQueue _queue; + + public QueueMerkleAnchorScheduler(LedgerAnchorQueue queue) + { + _queue = queue ?? throw new ArgumentNullException(nameof(queue)); + } + + public Task EnqueueAsync(LedgerEventRecord record, CancellationToken cancellationToken) + => _queue.EnqueueAsync(record, cancellationToken).AsTask(); +} diff --git a/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/scripts/compute_hashes.py b/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/scripts/compute_hashes.py new file mode 100644 index 000000000..ae729b849 --- /dev/null +++ b/src/Findings/StellaOps.Findings.Ledger/tools/LedgerReplayHarness/scripts/compute_hashes.py @@ -0,0 +1,43 @@ +import json +import sys +from hashlib import sha256 + +EMPTY_PREV = "0" * 64 + + +def canonical(obj): + return json.dumps(obj, separators=(",", ":"), sort_keys=True) + + +def hash_event(payload, sequence_no): + canonical_json = canonical(payload).encode() + event_hash = sha256(canonical_json + str(sequence_no).encode()).hexdigest() + merkle_leaf = sha256(event_hash.encode()).hexdigest() + return event_hash, merkle_leaf + + +def main(path): + out_lines = [] + last_hash = {} + with open(path, "r") as f: + events = [json.loads(line) for line in f if line.strip()] + events.sort(key=lambda e: (e["chain_id"], e["sequence_no"])) + for e in events: + prev = e.get("previous_hash") or last_hash.get(e["chain_id"], EMPTY_PREV) + payload = e.get("payload") or e + event_hash, leaf = hash_event(payload, e["sequence_no"]) + e["event_hash"] = event_hash + e["merkle_leaf_hash"] = leaf + e["previous_hash"] = prev + last_hash[e["chain_id"]] = event_hash + out_lines.append(json.dumps(e)) + with open(path, "w") as f: + for line in out_lines: + f.write(line + "\n") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("usage: compute_hashes.py ") + sys.exit(1) + main(sys.argv[1]) diff --git a/src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/HarnessRunnerTests.cs b/src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/HarnessRunnerTests.cs new file mode 100644 index 000000000..e5b5a85ef --- /dev/null +++ b/src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/HarnessRunnerTests.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using LedgerReplayHarness; +using FluentAssertions; +using Xunit; + +namespace StellaOps.Findings.Ledger.Tests; + +public class HarnessRunnerTests +{ + [Fact] + public async Task HarnessRunner_WritesReportAndValidatesHashes() + { + var fixturePath = Path.Combine(AppContext.BaseDirectory, "fixtures", "sample.ndjson"); + var tempReport = Path.GetTempFileName(); + + try + { + var exitCode = await HarnessRunner.RunAsync(new[] { fixturePath }, "tenant-test", tempReport); + exitCode.Should().Be(0); + + var json = await File.ReadAllTextAsync(tempReport); + using var doc = JsonDocument.Parse(json); + doc.RootElement.GetProperty("eventsWritten").GetInt64().Should().BeGreaterThan(0); + doc.RootElement.GetProperty("status").GetString().Should().Be("pass"); + doc.RootElement.GetProperty("tenant").GetString().Should().Be("tenant-test"); + doc.RootElement.GetProperty("hashSummary").GetProperty("uniqueEventHashes").GetInt32().Should().Be(1); + doc.RootElement.GetProperty("hashSummary").GetProperty("uniqueMerkleLeaves").GetInt32().Should().Be(1); + } + finally + { + if (File.Exists(tempReport)) + { + File.Delete(tempReport); + } + } + } +} diff --git a/src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/LedgerMetricsTests.cs b/src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/LedgerMetricsTests.cs new file mode 100644 index 000000000..67e066c51 --- /dev/null +++ b/src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/LedgerMetricsTests.cs @@ -0,0 +1,223 @@ +using System.Diagnostics.Metrics; +using System.Linq; +using FluentAssertions; +using StellaOps.Findings.Ledger.Observability; +using Xunit; + +namespace StellaOps.Findings.Ledger.Tests; + +public class LedgerMetricsTests +{ + [Fact] + public void ProjectionLagGauge_RecordsLatestPerTenant() + { + using var listener = CreateListener(); + var measurements = new List>(); + + listener.SetMeasurementEventCallback((instrument, measurement, tags, state) => + { + if (instrument.Name == "ledger_projection_lag_seconds") + { + measurements.Add(measurement); + } + }); + + LedgerMetrics.RecordProjectionLag(TimeSpan.FromSeconds(42), "tenant-a"); + + listener.RecordObservableInstruments(); + + var measurement = measurements.Should().ContainSingle().Subject; + measurement.Value.Should().BeApproximately(42, precision: 0.001); + measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) + .Should().Contain(new KeyValuePair("tenant", "tenant-a")); + } + + [Fact] + public void MerkleAnchorDuration_EmitsHistogramMeasurement() + { + using var listener = CreateListener(); + var measurements = new List>(); + + listener.SetMeasurementEventCallback((instrument, measurement, tags, state) => + { + if (instrument.Name == "ledger_merkle_anchor_duration_seconds") + { + measurements.Add(measurement); + } + }); + + LedgerMetrics.RecordMerkleAnchorDuration(TimeSpan.FromSeconds(1.5), "tenant-b"); + + var measurement = measurements.Should().ContainSingle().Subject; + measurement.Value.Should().BeApproximately(1.5, precision: 0.001); + measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) + .Should().Contain(new KeyValuePair("tenant", "tenant-b")); + } + + [Fact] + public void MerkleAnchorFailure_IncrementsCounter() + { + using var listener = CreateListener(); + var measurements = new List>(); + + listener.SetMeasurementEventCallback((instrument, measurement, tags, state) => + { + if (instrument.Name == "ledger_merkle_anchor_failures_total") + { + measurements.Add(measurement); + } + }); + + LedgerMetrics.RecordMerkleAnchorFailure("tenant-c", "persist_failure"); + + var measurement = measurements.Should().ContainSingle().Subject; + measurement.Value.Should().Be(1); + var tags = measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + tags.Should().Contain(new KeyValuePair("tenant", "tenant-c")); + tags.Should().Contain(new KeyValuePair("reason", "persist_failure")); + } + + [Fact] + public void AttachmentFailure_IncrementsCounter() + { + using var listener = CreateListener(); + var measurements = new List>(); + + listener.SetMeasurementEventCallback((instrument, measurement, tags, state) => + { + if (instrument.Name == "ledger_attachments_encryption_failures_total") + { + measurements.Add(measurement); + } + }); + + LedgerMetrics.RecordAttachmentFailure("tenant-d", "encrypt"); + + var measurement = measurements.Should().ContainSingle().Subject; + measurement.Value.Should().Be(1); + var tags = measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + tags.Should().Contain(new KeyValuePair("tenant", "tenant-d")); + tags.Should().Contain(new KeyValuePair("stage", "encrypt")); + } + + [Fact] + public void BacklogGauge_ReflectsOutstandingQueue() + { + using var listener = CreateListener(); + var measurements = new List>(); + + // Reset + LedgerMetrics.DecrementBacklog("tenant-q"); + + LedgerMetrics.IncrementBacklog("tenant-q"); + LedgerMetrics.IncrementBacklog("tenant-q"); + LedgerMetrics.DecrementBacklog("tenant-q"); + + listener.SetMeasurementEventCallback((instrument, measurement, tags, state) => + { + if (instrument.Name == "ledger_ingest_backlog_events") + { + measurements.Add(measurement); + } + }); + + listener.RecordObservableInstruments(); + + var measurement = measurements.Should().ContainSingle().Subject; + measurement.Value.Should().Be(1); + measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) + .Should().Contain(new KeyValuePair("tenant", "tenant-q")); + } + + [Fact] + public void ProjectionRebuildHistogram_RecordsScenarioTags() + { + using var listener = CreateListener(); + var measurements = new List>(); + + listener.SetMeasurementEventCallback((instrument, measurement, tags, state) => + { + if (instrument.Name == "ledger_projection_rebuild_seconds") + { + measurements.Add(measurement); + } + }); + + LedgerMetrics.RecordProjectionRebuild(TimeSpan.FromSeconds(3.2), "tenant-r", "replay"); + + var measurement = measurements.Should().ContainSingle().Subject; + measurement.Value.Should().BeApproximately(3.2, 0.001); + var tags = measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + tags.Should().Contain(new KeyValuePair("tenant", "tenant-r")); + tags.Should().Contain(new KeyValuePair("scenario", "replay")); + } + + [Fact] + public void DbConnectionsGauge_TracksRoleCounts() + { + using var listener = CreateListener(); + var measurements = new List>(); + + // Reset + LedgerMetrics.DecrementDbConnection("writer"); + + LedgerMetrics.IncrementDbConnection("writer"); + + listener.SetMeasurementEventCallback((instrument, measurement, tags, state) => + { + if (instrument.Name == "ledger_db_connections_active") + { + measurements.Add(measurement); + } + }); + + listener.RecordObservableInstruments(); + + var measurement = measurements.Should().ContainSingle().Subject; + measurement.Value.Should().Be(1); + measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) + .Should().Contain(new KeyValuePair("role", "writer")); + + LedgerMetrics.DecrementDbConnection("writer"); + } + + [Fact] + public void VersionInfoGauge_EmitsConstantOne() + { + using var listener = CreateListener(); + var measurements = new List>(); + + listener.SetMeasurementEventCallback((instrument, measurement, tags, state) => + { + if (instrument.Name == "ledger_app_version_info") + { + measurements.Add(measurement); + } + }); + + listener.RecordObservableInstruments(); + + var measurement = measurements.Should().ContainSingle().Subject; + measurement.Value.Should().Be(1); + var tags = measurement.Tags.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + tags.Should().ContainKey("version"); + tags.Should().ContainKey("git_sha"); + } + + private static MeterListener CreateListener() + { + var listener = new MeterListener + { + InstrumentPublished = (instrument, l) => + { + if (instrument.Meter.Name == "StellaOps.Findings.Ledger") + { + l.EnableMeasurementEvents(instrument); + } + } + }; + + listener.Start(); + return listener; + } +} diff --git a/src/Findings/tools/LedgerReplayHarness/HarnessRunner.cs b/src/Findings/tools/LedgerReplayHarness/HarnessRunner.cs new file mode 100644 index 000000000..f09813809 --- /dev/null +++ b/src/Findings/tools/LedgerReplayHarness/HarnessRunner.cs @@ -0,0 +1,148 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using StellaOps.Findings.Ledger.Domain; +using StellaOps.Findings.Ledger.Hashing; + +namespace LedgerReplayHarness; + +public sealed class HarnessRunner +{ + private readonly ILedgerClient _client; + private readonly int _maxParallel; + + public HarnessRunner(ILedgerClient client, int maxParallel = 4) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _maxParallel = maxParallel <= 0 ? 1 : maxParallel; + } + + public async Task RunAsync(IEnumerable fixtures, string tenant, string reportPath, CancellationToken cancellationToken) + { + if (fixtures is null || !fixtures.Any()) + { + throw new ArgumentException("At least one fixture is required.", nameof(fixtures)); + } + + var stats = new HarnessStats(); + + tenant = string.IsNullOrWhiteSpace(tenant) ? "default" : tenant; + reportPath = string.IsNullOrWhiteSpace(reportPath) ? "harness-report.json" : reportPath; + + var eventCount = 0L; + var hashesValid = true; + DateTimeOffset? earliest = null; + DateTimeOffset? latest = null; + var latencies = new List(); + var leafHashes = new List(); + string? expectedMerkleRoot = null; + var latencies = new ConcurrentBag(); + var swTotal = Stopwatch.StartNew(); + + var throttler = new TaskThrottler(_maxParallel); + + foreach (var fixture in fixtures) + { + await foreach (var line in ReadLinesAsync(fixture, cancellationToken)) + { + if (string.IsNullOrWhiteSpace(line)) continue; + var node = JsonNode.Parse(line)?.AsObject(); + if (node is null) continue; + + eventCount++; + var recordedAt = node["recorded_at"]?.GetValue() ?? DateTimeOffset.UtcNow; + earliest = earliest is null ? recordedAt : DateTimeOffset.Compare(recordedAt, earliest.Value) < 0 ? recordedAt : earliest; + latest = latest is null + ? recordedAt + : DateTimeOffset.Compare(recordedAt, latest.Value) > 0 ? recordedAt : latest; + + if (node["canonical_envelope"] is JsonObject envelope && node["sequence_no"] is not null) + { + var seq = node["sequence_no"]!.GetValue(); + var computed = LedgerHashing.ComputeHashes(envelope, seq); + var expected = node["event_hash"]?.GetValue(); + if (!string.IsNullOrEmpty(expected) && !string.Equals(expected, computed.EventHash, StringComparison.Ordinal)) + { + hashesValid = false; + } + + stats.UpdateHashes(computed.EventHash, computed.MerkleLeafHash); + leafHashes.Add(computed.MerkleLeafHash); + expectedMerkleRoot ??= node["merkle_root"]?.GetValue(); + + // enqueue for concurrent append + var record = new LedgerEventRecord( + tenant, + envelope["chain_id"]?.GetValue() ?? Guid.Empty, + seq, + envelope["event_id"]?.GetValue() ?? Guid.Empty, + envelope["event_type"]?.GetValue() ?? string.Empty, + envelope["policy_version"]?.GetValue() ?? string.Empty, + envelope["finding_id"]?.GetValue() ?? string.Empty, + envelope["artifact_id"]?.GetValue() ?? string.Empty, + envelope["source_run_id"]?.GetValue(), + envelope["actor_id"]?.GetValue() ?? "system", + envelope["actor_type"]?.GetValue() ?? "system", + envelope["occurred_at"]?.GetValue() ?? recordedAt, + recordedAt, + envelope, + computed.EventHash, + envelope["previous_hash"]?.GetValue() ?? string.Empty, + computed.MerkleLeafHash, + computed.CanonicalJson); + + // fire-and-track latency + await throttler.RunAsync(async () => + { + var sw = Stopwatch.StartNew(); + await _client.AppendAsync(record, cancellationToken).ConfigureAwait(false); + sw.Stop(); + latencies.Add(sw.Elapsed.TotalMilliseconds); + }, cancellationToken).ConfigureAwait(false); + } + } + } + + await throttler.DrainAsync(cancellationToken).ConfigureAwait(false); + swTotal.Stop(); + + var latencyArray = latencies.ToArray(); + Array.Sort(latencyArray); + double p95 = latencyArray.Length == 0 ? 0 : latencyArray[(int)Math.Ceiling(latencyArray.Length * 0.95) - 1]; + + string? computedRoot = leafHashes.Count == 0 ? null : MerkleCalculator.ComputeRoot(leafHashes); + var merkleOk = expectedMerkleRoot is null || string.Equals(expectedMerkleRoot, computedRoot, StringComparison.OrdinalIgnoreCase); + + var report = new + { + tenant, + fixtures = fixtures.ToArray(), + eventsWritten = eventCount, + durationSeconds = Math.Max(swTotal.Elapsed.TotalSeconds, (latest - earliest)?.TotalSeconds ?? 0), + throughputEps = swTotal.Elapsed.TotalSeconds > 0 ? eventCount / swTotal.Elapsed.TotalSeconds : 0, + latencyP95Ms = p95, + projectionLagMaxSeconds = 0, + cpuPercentMax = 0, + memoryMbMax = 0, + status = hashesValid && merkleOk ? "pass" : "fail", + timestamp = DateTimeOffset.UtcNow.ToString("O"), + hashSummary = stats.ToReport(), + merkleRoot = computedRoot, + merkleExpected = expectedMerkleRoot + }; + + var json = JsonSerializer.Serialize(report, new JsonSerializerOptions { WriteIndented = true }); + await File.WriteAllTextAsync(reportPath, json); + return hashesValid && merkleOk ? 0 : 1; + } + + private static async IAsyncEnumerable ReadLinesAsync(string path, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) + { + await using var stream = File.OpenRead(path); + using var reader = new StreamReader(stream); + string? line; + while (!reader.EndOfStream && !cancellationToken.IsCancellationRequested && (line = await reader.ReadLineAsync()) is not null) + { + yield return line; + } + } +} diff --git a/src/Findings/tools/LedgerReplayHarness/HarnessStats.cs b/src/Findings/tools/LedgerReplayHarness/HarnessStats.cs new file mode 100644 index 000000000..f1a8f15d1 --- /dev/null +++ b/src/Findings/tools/LedgerReplayHarness/HarnessStats.cs @@ -0,0 +1,26 @@ +namespace LedgerReplayHarness; + +internal sealed class HarnessStats +{ + private readonly HashSet _eventHashes = new(StringComparer.OrdinalIgnoreCase); + private readonly HashSet _leafHashes = new(StringComparer.OrdinalIgnoreCase); + + public void UpdateHashes(string eventHash, string leafHash) + { + if (!string.IsNullOrWhiteSpace(eventHash)) + { + _eventHashes.Add(eventHash); + } + + if (!string.IsNullOrWhiteSpace(leafHash)) + { + _leafHashes.Add(leafHash); + } + } + + public object ToReport() => new + { + uniqueEventHashes = _eventHashes.Count, + uniqueMerkleLeaves = _leafHashes.Count + }; +} diff --git a/src/Findings/tools/LedgerReplayHarness/ILedgerClient.cs b/src/Findings/tools/LedgerReplayHarness/ILedgerClient.cs new file mode 100644 index 000000000..b02a7b13d --- /dev/null +++ b/src/Findings/tools/LedgerReplayHarness/ILedgerClient.cs @@ -0,0 +1,8 @@ +using StellaOps.Findings.Ledger.Domain; + +namespace LedgerReplayHarness; + +public interface ILedgerClient +{ + Task AppendAsync(LedgerEventRecord record, CancellationToken cancellationToken); +} diff --git a/src/Findings/tools/LedgerReplayHarness/InMemoryLedgerClient.cs b/src/Findings/tools/LedgerReplayHarness/InMemoryLedgerClient.cs new file mode 100644 index 000000000..01342bce9 --- /dev/null +++ b/src/Findings/tools/LedgerReplayHarness/InMemoryLedgerClient.cs @@ -0,0 +1,15 @@ +using System.Collections.Concurrent; +using StellaOps.Findings.Ledger.Domain; + +namespace LedgerReplayHarness; + +public sealed class InMemoryLedgerClient : ILedgerClient +{ + private readonly ConcurrentDictionary<(string Tenant, Guid EventId), LedgerEventRecord> _store = new(); + + public Task AppendAsync(LedgerEventRecord record, CancellationToken cancellationToken) + { + _store.TryAdd((record.TenantId, record.EventId), record); + return Task.CompletedTask; + } +} diff --git a/src/Findings/tools/LedgerReplayHarness/LedgerReplayHarness.csproj b/src/Findings/tools/LedgerReplayHarness/LedgerReplayHarness.csproj new file mode 100644 index 000000000..9920ea8a8 --- /dev/null +++ b/src/Findings/tools/LedgerReplayHarness/LedgerReplayHarness.csproj @@ -0,0 +1,14 @@ + + + Exe + net10.0 + enable + enable + + + + + + + + diff --git a/src/Findings/tools/LedgerReplayHarness/MerkleCalculator.cs b/src/Findings/tools/LedgerReplayHarness/MerkleCalculator.cs new file mode 100644 index 000000000..e512481d7 --- /dev/null +++ b/src/Findings/tools/LedgerReplayHarness/MerkleCalculator.cs @@ -0,0 +1,41 @@ +using System.Security.Cryptography; +using System.Text; + +namespace LedgerReplayHarness; + +internal static class MerkleCalculator +{ + public static string ComputeRoot(IReadOnlyList leafHashes) + { + if (leafHashes is null || leafHashes.Count == 0) + { + throw new ArgumentException("At least one leaf hash is required.", nameof(leafHashes)); + } + + var level = leafHashes.Select(Normalize).ToList(); + while (level.Count > 1) + { + var next = new List((level.Count + 1) / 2); + for (int i = 0; i < level.Count; i += 2) + { + var left = level[i]; + var right = i + 1 < level.Count ? level[i + 1] : level[i]; + next.Add(HashPair(left, right)); + } + level = next; + } + + return level[0]; + } + + private static string Normalize(string hex) + => hex?.Trim().ToLowerInvariant() ?? string.Empty; + + private static string HashPair(string left, string right) + { + using var sha = SHA256.Create(); + var data = Encoding.UTF8.GetBytes(left + right); + var hash = sha.ComputeHash(data); + return Convert.ToHexString(hash).ToLowerInvariant(); + } +} diff --git a/src/Findings/tools/LedgerReplayHarness/Program.cs b/src/Findings/tools/LedgerReplayHarness/Program.cs new file mode 100644 index 000000000..7bfa6c53a --- /dev/null +++ b/src/Findings/tools/LedgerReplayHarness/Program.cs @@ -0,0 +1,22 @@ +using System.CommandLine; +using LedgerReplayHarness; + +var fixtureOption = new Option("--fixture", "NDJSON fixture path(s)") { IsRequired = true, AllowMultipleArgumentsPerToken = true }; +var tenantOption = new Option("--tenant", () => "default", "Tenant identifier"); +var reportOption = new Option("--report", () => "harness-report.json", "Path to write JSON report"); +var parallelOption = new Option("--maxParallel", () => 4, "Maximum parallelism when sending events"); + +var root = new RootCommand("Findings Ledger replay & determinism harness"); +root.AddOption(fixtureOption); +root.AddOption(tenantOption); +root.AddOption(reportOption); +root.AddOption(parallelOption); + +root.SetHandler(async (fixtures, tenant, report, maxParallel) => +{ + var runner = new HarnessRunner(new InMemoryLedgerClient(), maxParallel); + var exitCode = await runner.RunAsync(fixtures, tenant, report, CancellationToken.None); + Environment.Exit(exitCode); +}, fixtureOption, tenantOption, reportOption, parallelOption); + +return await root.InvokeAsync(args); diff --git a/src/Findings/tools/LedgerReplayHarness/TaskThrottler.cs b/src/Findings/tools/LedgerReplayHarness/TaskThrottler.cs new file mode 100644 index 000000000..f257d4329 --- /dev/null +++ b/src/Findings/tools/LedgerReplayHarness/TaskThrottler.cs @@ -0,0 +1,36 @@ +namespace LedgerReplayHarness; + +internal sealed class TaskThrottler +{ + private readonly SemaphoreSlim _semaphore; + private readonly List _tasks = new(); + + public TaskThrottler(int maxDegreeOfParallelism) + { + _semaphore = new SemaphoreSlim(maxDegreeOfParallelism > 0 ? maxDegreeOfParallelism : 1); + } + + public async Task RunAsync(Func taskFactory, CancellationToken cancellationToken) + { + await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + var task = Task.Run(async () => + { + try + { + await taskFactory().ConfigureAwait(false); + } + finally + { + _semaphore.Release(); + } + }, cancellationToken); + lock (_tasks) _tasks.Add(task); + } + + public async Task DrainAsync(CancellationToken cancellationToken) + { + Task[] pending; + lock (_tasks) pending = _tasks.ToArray(); + await Task.WhenAll(pending).WaitAsync(cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/AttestationTemplateCoverageTests.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/AttestationTemplateCoverageTests.cs new file mode 100644 index 000000000..a0967d5d1 --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/AttestationTemplateCoverageTests.cs @@ -0,0 +1,77 @@ +using System.Text.Json; +using Xunit; + +namespace StellaOps.Notifier.Tests; + +public sealed class AttestationTemplateCoverageTests +{ + private static readonly string RepoRoot = LocateRepoRoot(); + + [Fact] + public void Attestation_templates_cover_required_channels() + { + var directory = Path.Combine(RepoRoot, "offline", "notifier", "templates", "attestation"); + Assert.True(Directory.Exists(directory), $"Expected template directory at {directory}"); + + var templates = Directory + .GetFiles(directory, "*.template.json") + .Select(path => new + { + Path = path, + Document = JsonDocument.Parse(File.ReadAllText(path)).RootElement + }) + .ToList(); + + var required = new Dictionary + { + ["tmpl-attest-verify-fail"] = new[] { "slack", "email", "webhook" }, + ["tmpl-attest-expiry-warning"] = new[] { "email", "slack" }, + ["tmpl-attest-key-rotation"] = new[] { "email", "webhook" }, + ["tmpl-attest-transparency-anomaly"] = new[] { "slack", "webhook" } + }; + + foreach (var pair in required) + { + var matches = templates.Where(t => t.Document.GetProperty("key").GetString() == pair.Key); + var channels = matches + .Select(t => t.Document.GetProperty("channelType").GetString() ?? string.Empty) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + + var missing = pair.Value.Where(requiredChannel => !channels.Contains(requiredChannel)).ToArray(); + Assert.True(missing.Length == 0, $"{pair.Key} missing channels: {string.Join(", ", missing)}"); + } + } + + [Fact] + public void Attestation_templates_include_schema_and_locale_metadata() + { + var directory = Path.Combine(RepoRoot, "offline", "notifier", "templates", "attestation"); + Assert.True(Directory.Exists(directory), $"Expected template directory at {directory}"); + + foreach (var path in Directory.GetFiles(directory, "*.template.json")) + { + var document = JsonDocument.Parse(File.ReadAllText(path)).RootElement; + + Assert.True(document.TryGetProperty("schemaVersion", out var schemaVersion) && !string.IsNullOrWhiteSpace(schemaVersion.GetString()), $"schemaVersion missing for {Path.GetFileName(path)}"); + Assert.True(document.TryGetProperty("locale", out var locale) && !string.IsNullOrWhiteSpace(locale.GetString()), $"locale missing for {Path.GetFileName(path)}"); + Assert.True(document.TryGetProperty("key", out var key) && !string.IsNullOrWhiteSpace(key.GetString()), $"key missing for {Path.GetFileName(path)}"); + } + } + + private static string LocateRepoRoot() + { + var directory = AppContext.BaseDirectory; + while (directory != null) + { + var candidate = Path.Combine(directory, "offline", "notifier", "templates", "attestation"); + if (Directory.Exists(candidate)) + { + return directory; + } + + directory = Directory.GetParent(directory)?.FullName; + } + + throw new InvalidOperationException("Unable to locate repository root containing offline/notifier/templates/attestation."); + } +} diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/DeprecationTemplateTests.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/DeprecationTemplateTests.cs new file mode 100644 index 000000000..b468bbe8c --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/DeprecationTemplateTests.cs @@ -0,0 +1,66 @@ +using System.Text.Json; +using Xunit; + +namespace StellaOps.Notifier.Tests; + +public sealed class DeprecationTemplateTests +{ + [Fact] + public void Deprecation_templates_cover_slack_and_email() + { + var directory = LocateOfflineDeprecationDir(); + Assert.True(Directory.Exists(directory), $"Expected template directory at {directory}"); + + var templates = Directory + .GetFiles(directory, "*.template.json") + .Select(path => new + { + Path = path, + Document = JsonDocument.Parse(File.ReadAllText(path)).RootElement + }) + .ToList(); + + var channels = templates + .Where(t => t.Document.GetProperty("key").GetString() == "tmpl-api-deprecation") + .Select(t => t.Document.GetProperty("channelType").GetString() ?? string.Empty) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + + Assert.Contains("slack", channels); + Assert.Contains("email", channels); + } + + [Fact] + public void Deprecation_templates_require_core_metadata() + { + var directory = LocateOfflineDeprecationDir(); + Assert.True(Directory.Exists(directory), $"Expected template directory at {directory}"); + + foreach (var path in Directory.GetFiles(directory, "*.template.json")) + { + var document = JsonDocument.Parse(File.ReadAllText(path)).RootElement; + + Assert.True(document.TryGetProperty("metadata", out var meta), $"metadata missing for {Path.GetFileName(path)}"); + + // Ensure documented metadata keys are present for offline baseline. + Assert.True(meta.TryGetProperty("version", out _), $"metadata.version missing for {Path.GetFileName(path)}"); + Assert.True(meta.TryGetProperty("author", out _), $"metadata.author missing for {Path.GetFileName(path)}"); + } + } + + private static string LocateOfflineDeprecationDir() + { + var directory = AppContext.BaseDirectory; + while (directory != null) + { + var candidate = Path.Combine(directory, "offline", "notifier", "templates", "deprecation"); + if (Directory.Exists(candidate)) + { + return candidate; + } + + directory = Directory.GetParent(directory)?.FullName; + } + + throw new InvalidOperationException("Unable to locate offline/notifier/templates/deprecation directory."); + } +} diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/OpenApiEndpointTests.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/OpenApiEndpointTests.cs new file mode 100644 index 000000000..a75a05eeb --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/OpenApiEndpointTests.cs @@ -0,0 +1,87 @@ +using System.Net; +using Microsoft.AspNetCore.Mvc.Testing; +using StellaOps.Notifier.WebService; +using Xunit; + +namespace StellaOps.Notifier.Tests; + +public sealed class OpenApiEndpointTests : IClassFixture> +{ + private readonly HttpClient _client; + private readonly InMemoryPackApprovalRepository _packRepo = new(); + private readonly InMemoryLockRepository _lockRepo = new(); + private readonly InMemoryAuditRepository _auditRepo = new(); + + public OpenApiEndpointTests(WebApplicationFactory factory) + { + _client = factory + .WithWebHostBuilder(builder => + { + builder.ConfigureServices(services => + { + services.AddSingleton(_packRepo); + services.AddSingleton(_lockRepo); + services.AddSingleton(_auditRepo); + }); + }) + .CreateClient(); + } + + [Fact] + public async Task OpenApi_endpoint_serves_yaml_with_scope_header() + { + var response = await _client.GetAsync("/.well-known/openapi", TestContext.Current.CancellationToken); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("application/yaml", response.Content.Headers.ContentType?.MediaType); + Assert.True(response.Headers.TryGetValues("X-OpenAPI-Scope", out var values) && + values.Contains("notify")); + Assert.True(response.Headers.ETag is not null && response.Headers.ETag.Tag.Length > 2); + + var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); + Assert.Contains("openapi: 3.1.0", body); + Assert.Contains("/api/v1/notify/quiet-hours", body); + Assert.Contains("/api/v1/notify/incidents", body); + } + + [Fact] + public async Task Deprecation_headers_emitted_for_api_surface() + { + var response = await _client.GetAsync("/api/v1/notify/rules", TestContext.Current.CancellationToken); + + Assert.True(response.Headers.TryGetValues("Deprecation", out var depValues) && + depValues.Contains("true")); + Assert.True(response.Headers.TryGetValues("Sunset", out var sunsetValues) && + sunsetValues.Any()); + Assert.True(response.Headers.TryGetValues("Link", out var linkValues) && + linkValues.Any(v => v.Contains("rel=\"deprecation\""))); + } + + [Fact] + public async Task PackApprovals_endpoint_validates_missing_headers() + { + var content = new StringContent("""{"eventId":"00000000-0000-0000-0000-000000000001","issuedAt":"2025-11-17T16:00:00Z","kind":"pack.approval.granted","packId":"offline-kit","decision":"approved","actor":"task-runner"}""", Encoding.UTF8, "application/json"); + var response = await _client.PostAsync("/api/v1/notify/pack-approvals", content, TestContext.Current.CancellationToken); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + public async Task PackApprovals_endpoint_accepts_happy_path_and_echoes_resume_token() + { + var content = new StringContent("""{"eventId":"00000000-0000-0000-0000-000000000002","issuedAt":"2025-11-17T16:00:00Z","kind":"pack.approval.granted","packId":"offline-kit","decision":"approved","actor":"task-runner","resumeToken":"rt-ok"}""", Encoding.UTF8, "application/json"); + var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/notify/pack-approvals") + { + Content = content + }; + request.Headers.Add("X-StellaOps-Tenant", "tenant-a"); + request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString()); + + var response = await _client.SendAsync(request, TestContext.Current.CancellationToken); + + Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + Assert.True(response.Headers.TryGetValues("X-Resume-After", out var resumeValues) && + resumeValues.Contains("rt-ok")); + Assert.True(_packRepo.Exists("tenant-a", Guid.Parse("00000000-0000-0000-0000-000000000002"), "offline-kit")); + } +} diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/Support/InMemoryAuditRepository.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/Support/InMemoryAuditRepository.cs new file mode 100644 index 000000000..89e8d62e1 --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/Support/InMemoryAuditRepository.cs @@ -0,0 +1,30 @@ +using StellaOps.Notify.Storage.Mongo.Documents; +using StellaOps.Notify.Storage.Mongo.Repositories; + +namespace StellaOps.Notifier.Tests.Support; + +internal sealed class InMemoryAuditRepository : INotifyAuditRepository +{ + private readonly List _entries = new(); + + public Task AppendAsync(NotifyAuditEntryDocument entry, CancellationToken cancellationToken = default) + { + _entries.Add(entry); + return Task.CompletedTask; + } + + public Task> QueryAsync(string tenantId, DateTimeOffset? since, int? limit, CancellationToken cancellationToken = default) + { + var items = _entries + .Where(e => e.TenantId == tenantId && (!since.HasValue || e.Timestamp >= since.Value)) + .OrderByDescending(e => e.Timestamp) + .ToList(); + + if (limit is > 0) + { + items = items.Take(limit.Value).ToList(); + } + + return Task.FromResult>(items); + } +} diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/Support/InMemoryPackApprovalRepository.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/Support/InMemoryPackApprovalRepository.cs new file mode 100644 index 000000000..4628ad9af --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.Tests/Support/InMemoryPackApprovalRepository.cs @@ -0,0 +1,18 @@ +using StellaOps.Notify.Storage.Mongo.Documents; +using StellaOps.Notify.Storage.Mongo.Repositories; + +namespace StellaOps.Notifier.Tests.Support; + +internal sealed class InMemoryPackApprovalRepository : INotifyPackApprovalRepository +{ + private readonly Dictionary<(string TenantId, Guid EventId, string PackId), PackApprovalDocument> _records = new(); + + public Task UpsertAsync(PackApprovalDocument document, CancellationToken cancellationToken = default) + { + _records[(document.TenantId, document.EventId, document.PackId)] = document; + return Task.CompletedTask; + } + + public bool Exists(string tenantId, Guid eventId, string packId) + => _records.ContainsKey((tenantId, eventId, packId)); +} diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Contracts/PackApprovalRequest.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Contracts/PackApprovalRequest.cs new file mode 100644 index 000000000..0f61a963d --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Contracts/PackApprovalRequest.cs @@ -0,0 +1,45 @@ +using System.Text.Json.Serialization; + +namespace StellaOps.Notifier.WebService.Contracts; + +public sealed class PackApprovalRequest +{ + [JsonPropertyName("eventId")] + public Guid EventId { get; init; } + + [JsonPropertyName("issuedAt")] + public DateTimeOffset IssuedAt { get; init; } + + [JsonPropertyName("kind")] + public string Kind { get; init; } = string.Empty; + + [JsonPropertyName("packId")] + public string PackId { get; init; } = string.Empty; + + [JsonPropertyName("policy")] + public PackApprovalPolicy? Policy { get; init; } + + [JsonPropertyName("decision")] + public string Decision { get; init; } = string.Empty; + + [JsonPropertyName("actor")] + public string Actor { get; init; } = string.Empty; + + [JsonPropertyName("resumeToken")] + public string? ResumeToken { get; init; } + + [JsonPropertyName("summary")] + public string? Summary { get; init; } + + [JsonPropertyName("labels")] + public Dictionary? Labels { get; init; } +} + +public sealed class PackApprovalPolicy +{ + [JsonPropertyName("id")] + public string? Id { get; init; } + + [JsonPropertyName("version")] + public string? Version { get; init; } +} diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Program.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Program.cs index d817c4907..ba330616e 100644 --- a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Program.cs +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Program.cs @@ -1,24 +1,141 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using StellaOps.Notify.Storage.Mongo; -using StellaOps.Notifier.WebService.Setup; - -var builder = WebApplication.CreateBuilder(args); - -builder.Configuration - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddEnvironmentVariables(prefix: "NOTIFIER_"); - -var mongoSection = builder.Configuration.GetSection("notifier:storage:mongo"); -builder.Services.AddNotifyMongoStorage(mongoSection); - -builder.Services.AddHealthChecks(); -builder.Services.AddHostedService(); - -var app = builder.Build(); - -app.MapHealthChecks("/healthz"); - -app.Run(); +using System.Text.Json; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using StellaOps.Notifier.WebService.Contracts; +using StellaOps.Notifier.WebService.Setup; +using StellaOps.Notify.Storage.Mongo; +using StellaOps.Notify.Storage.Mongo.Documents; +using StellaOps.Notify.Storage.Mongo.Repositories; + +var builder = WebApplication.CreateBuilder(args); + +builder.Configuration + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables(prefix: "NOTIFIER_"); + +var mongoSection = builder.Configuration.GetSection("notifier:storage:mongo"); +builder.Services.AddNotifyMongoStorage(mongoSection); +builder.Services.AddSingleton(); + +builder.Services.AddHealthChecks(); +builder.Services.AddHostedService(); + +var app = builder.Build(); + +app.MapHealthChecks("/healthz"); + +// Deprecation headers for retiring v1 APIs (RFC 8594 / IETF Sunset) +app.Use(async (context, next) => +{ + if (context.Request.Path.StartsWithSegments("/api/v1", StringComparison.OrdinalIgnoreCase)) + { + context.Response.Headers["Deprecation"] = "true"; + context.Response.Headers["Sunset"] = "Tue, 31 Mar 2026 00:00:00 GMT"; + context.Response.Headers["Link"] = + "; rel=\"deprecation\"; type=\"text/html\""; + } + + await next().ConfigureAwait(false); +}); + +app.MapPost("/api/v1/notify/pack-approvals", async ( + HttpContext context, + PackApprovalRequest request, + INotifyLockRepository locks, + INotifyPackApprovalRepository packApprovals, + INotifyAuditRepository audit, + TimeProvider timeProvider) => +{ + var tenantId = context.Request.Headers["X-StellaOps-Tenant"].ToString(); + if (string.IsNullOrWhiteSpace(tenantId)) + { + return Results.BadRequest(Error("tenant_missing", "X-StellaOps-Tenant header is required.", context)); + } + + var idempotencyKey = context.Request.Headers["Idempotency-Key"].ToString(); + if (string.IsNullOrWhiteSpace(idempotencyKey)) + { + return Results.BadRequest(Error("idempotency_key_missing", "Idempotency-Key header is required.", context)); + } + + if (request.EventId == Guid.Empty || string.IsNullOrWhiteSpace(request.PackId) || + string.IsNullOrWhiteSpace(request.Kind) || string.IsNullOrWhiteSpace(request.Decision) || + string.IsNullOrWhiteSpace(request.Actor)) + { + return Results.BadRequest(Error("invalid_request", "eventId, packId, kind, decision, actor are required.", context)); + } + + var lockKey = $"pack-approvals|{tenantId}|{idempotencyKey}"; + var ttl = TimeSpan.FromMinutes(15); + var reserved = await locks.TryAcquireAsync(tenantId, lockKey, "pack-approvals", ttl, context.RequestAborted) + .ConfigureAwait(false); + + if (!reserved) + { + return Results.StatusCode(StatusCodes.Status200OK); + } + + var document = new PackApprovalDocument + { + TenantId = tenantId, + EventId = request.EventId, + PackId = request.PackId, + Kind = request.Kind, + Decision = request.Decision, + Actor = request.Actor, + IssuedAt = request.IssuedAt, + PolicyId = request.Policy?.Id, + PolicyVersion = request.Policy?.Version, + ResumeToken = request.ResumeToken, + Summary = request.Summary, + Labels = request.Labels, + CreatedAt = timeProvider.GetUtcNow() + }; + + await packApprovals.UpsertAsync(document, context.RequestAborted).ConfigureAwait(false); + + var auditEntry = new NotifyAuditEntryDocument + { + TenantId = tenantId, + Actor = request.Actor, + Action = "pack.approval.ingested", + EntityId = request.PackId, + EntityType = "pack-approval", + Timestamp = timeProvider.GetUtcNow(), + Payload = MongoDB.Bson.Serialization.BsonSerializer.Deserialize(JsonSerializer.Serialize(request)) + }; + + await audit.AppendAsync(auditEntry, context.RequestAborted).ConfigureAwait(false); + + if (!string.IsNullOrWhiteSpace(request.ResumeToken)) + { + context.Response.Headers["X-Resume-After"] = request.ResumeToken; + } + + return Results.Accepted(); +}); + +app.MapGet("/.well-known/openapi", (HttpContext context, OpenApiDocumentCache cache) => +{ + context.Response.Headers.CacheControl = "public, max-age=300"; + context.Response.Headers["X-OpenAPI-Scope"] = "notify"; + context.Response.Headers.ETag = $"\"{cache.Sha256}\""; + return Results.Content(cache.Document, "application/yaml"); +}); + +app.Run(); + +public partial class Program; + +static object Error(string code, string message, HttpContext context) => new +{ + error = new + { + code, + message, + traceId = context.TraceIdentifier + } +}; diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Setup/OpenApiDocumentCache.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Setup/OpenApiDocumentCache.cs new file mode 100644 index 000000000..2fcaa80c7 --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Setup/OpenApiDocumentCache.cs @@ -0,0 +1,28 @@ +using System.Text; + +namespace StellaOps.Notifier.WebService.Setup; + +public sealed class OpenApiDocumentCache +{ + private readonly string _document; + private readonly string _hash; + + public OpenApiDocumentCache(IHostEnvironment environment) + { + var path = Path.Combine(environment.ContentRootPath, "openapi", "notify-openapi.yaml"); + if (!File.Exists(path)) + { + throw new FileNotFoundException("OpenAPI document not found.", path); + } + + _document = File.ReadAllText(path, Encoding.UTF8); + + using var sha = System.Security.Cryptography.SHA256.Create(); + var bytes = Encoding.UTF8.GetBytes(_document); + _hash = Convert.ToHexString(sha.ComputeHash(bytes)).ToLowerInvariant(); + } + + public string Document => _document; + + public string Sha256 => _hash; +} diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Setup/WebServiceAssemblyMarker.cs b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Setup/WebServiceAssemblyMarker.cs new file mode 100644 index 000000000..1107f3ffd --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Setup/WebServiceAssemblyMarker.cs @@ -0,0 +1,6 @@ +namespace StellaOps.Notifier.WebService; + +/// +/// Marker type used for testing/hosting the web application. +/// +public sealed class WebServiceAssemblyMarker; diff --git a/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/openapi/notify-openapi.yaml b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/openapi/notify-openapi.yaml new file mode 100644 index 000000000..f4f3d2f3b --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/openapi/notify-openapi.yaml @@ -0,0 +1,501 @@ +# OpenAPI 3.1 specification for StellaOps Notifier WebService (draft) +openapi: 3.1.0 +info: + title: StellaOps Notifier API + version: 0.6.0-draft + description: | + Contract for Notifications Studio (Notifier) covering rules, templates, incidents, + and quiet hours. Uses the platform error envelope and tenant header `X-StellaOps-Tenant`. +servers: + - url: https://api.stellaops.example.com + description: Production + - url: https://api.dev.stellaops.example.com + description: Development +security: + - oauth2: [notify.viewer] + - oauth2: [notify.operator] + - oauth2: [notify.admin] +paths: + /api/v1/notify/rules: + get: + summary: List notification rules + tags: [Rules] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/PageToken' + responses: + '200': + description: Paginated rule list + content: + application/json: + schema: + type: object + properties: + items: + type: array + items: { $ref: '#/components/schemas/NotifyRule' } + nextPageToken: + type: string + examples: + default: + value: + items: + - ruleId: rule-critical + tenantId: tenant-dev + name: Critical scanner verdicts + enabled: true + match: + eventKinds: [scanner.report.ready] + minSeverity: critical + actions: + - actionId: act-slack-critical + channel: chn-slack-soc + template: tmpl-critical + digest: instant + nextPageToken: null + default: + $ref: '#/components/responses/Error' + post: + summary: Create a notification rule + tags: [Rules] + parameters: + - $ref: '#/components/parameters/Tenant' + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyRule' } + examples: + create-rule: + value: + ruleId: rule-attest-fail + tenantId: tenant-dev + name: Attestation failures → SOC + enabled: true + match: + eventKinds: [attestor.verification.failed] + actions: + - actionId: act-soc + channel: chn-webhook-soc + template: tmpl-attest-verify-fail + responses: + '201': + description: Rule created + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyRule' } + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/rules/{ruleId}: + get: + summary: Fetch a rule + tags: [Rules] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/RuleId' + responses: + '200': + description: Rule + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyRule' } + default: + $ref: '#/components/responses/Error' + patch: + summary: Update a rule (partial) + tags: [Rules] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/RuleId' + requestBody: + required: true + content: + application/json: + schema: + type: object + description: JSON Merge Patch + responses: + '200': + description: Updated rule + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyRule' } + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/templates: + get: + summary: List templates + tags: [Templates] + parameters: + - $ref: '#/components/parameters/Tenant' + - name: key + in: query + description: Filter by template key + schema: { type: string } + responses: + '200': + description: Templates + content: + application/json: + schema: + type: array + items: { $ref: '#/components/schemas/NotifyTemplate' } + default: + $ref: '#/components/responses/Error' + post: + summary: Create a template + tags: [Templates] + parameters: + - $ref: '#/components/parameters/Tenant' + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyTemplate' } + responses: + '201': + description: Template created + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyTemplate' } + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/templates/{templateId}: + get: + summary: Fetch a template + tags: [Templates] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/TemplateId' + responses: + '200': + description: Template + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyTemplate' } + default: + $ref: '#/components/responses/Error' + patch: + summary: Update a template (partial) + tags: [Templates] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/TemplateId' + requestBody: + required: true + content: + application/json: + schema: + type: object + description: JSON Merge Patch + responses: + '200': + description: Updated template + content: + application/json: + schema: { $ref: '#/components/schemas/NotifyTemplate' } + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/incidents: + get: + summary: List incidents (paged) + tags: [Incidents] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/PageSize' + - $ref: '#/components/parameters/PageToken' + responses: + '200': + description: Incident page + content: + application/json: + schema: + type: object + properties: + items: + type: array + items: { $ref: '#/components/schemas/Incident' } + nextPageToken: { type: string } + default: + $ref: '#/components/responses/Error' + post: + summary: Raise an incident (ops/toggle/override) + tags: [Incidents] + parameters: + - $ref: '#/components/parameters/Tenant' + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/Incident' } + examples: + start-incident: + value: + incidentId: inc-telemetry-outage + kind: outage + severity: major + startedAt: 2025-11-17T04:02:00Z + shortDescription: "Telemetry pipeline degraded; burn-rate breach" + metadata: + source: slo-evaluator + responses: + '202': + description: Incident accepted + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/incidents/{incidentId}/ack: + post: + summary: Acknowledge an incident notification + tags: [Incidents] + parameters: + - $ref: '#/components/parameters/Tenant' + - $ref: '#/components/parameters/IncidentId' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + ackToken: + type: string + description: DSSE-signed acknowledgement token + responses: + '204': + description: Acknowledged + default: + $ref: '#/components/responses/Error' + + /api/v1/notify/quiet-hours: + get: + summary: Get quiet-hours schedule + tags: [QuietHours] + parameters: + - $ref: '#/components/parameters/Tenant' + responses: + '200': + description: Quiet hours schedule + content: + application/json: + schema: { $ref: '#/components/schemas/QuietHours' } + examples: + current: + value: + quietHoursId: qh-default + windows: + - timezone: UTC + days: [Mon, Tue, Wed, Thu, Fri] + start: "22:00" + end: "06:00" + exemptions: + - eventKinds: [attestor.verification.failed] + reason: "Always alert for attestation failures" + default: + $ref: '#/components/responses/Error' + post: + summary: Set quiet-hours schedule + tags: [QuietHours] + parameters: + - $ref: '#/components/parameters/Tenant' + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/QuietHours' } + responses: + '200': + description: Updated quiet hours + content: + application/json: + schema: { $ref: '#/components/schemas/QuietHours' } + default: + $ref: '#/components/responses/Error' + +components: + securitySchemes: + oauth2: + type: oauth2 + flows: + clientCredentials: + tokenUrl: https://auth.stellaops.example.com/oauth/token + scopes: + notify.viewer: Read-only Notifier access + notify.operator: Manage rules/templates/incidents within tenant + notify.admin: Tenant-scoped administration + parameters: + Tenant: + name: X-StellaOps-Tenant + in: header + required: true + description: Tenant slug + schema: { type: string } + PageSize: + name: pageSize + in: query + schema: { type: integer, minimum: 1, maximum: 200, default: 50 } + PageToken: + name: pageToken + in: query + schema: { type: string } + RuleId: + name: ruleId + in: path + required: true + schema: { type: string } + TemplateId: + name: templateId + in: path + required: true + schema: { type: string } + IncidentId: + name: incidentId + in: path + required: true + schema: { type: string } + + responses: + Error: + description: Standard error envelope + content: + application/json: + schema: { $ref: '#/components/schemas/ErrorEnvelope' } + examples: + validation: + value: + error: + code: validation_failed + message: "quietHours.windows[0].start must be HH:mm" + traceId: "f62f3c2b9c8e4c53" + + schemas: + ErrorEnvelope: + type: object + required: [error] + properties: + error: + type: object + required: [code, message, traceId] + properties: + code: { type: string } + message: { type: string } + traceId: { type: string } + + NotifyRule: + type: object + required: [ruleId, tenantId, name, match, actions] + properties: + ruleId: { type: string } + tenantId: { type: string } + name: { type: string } + description: { type: string } + enabled: { type: boolean, default: true } + match: { $ref: '#/components/schemas/RuleMatch' } + actions: + type: array + items: { $ref: '#/components/schemas/RuleAction' } + labels: + type: object + additionalProperties: { type: string } + metadata: + type: object + additionalProperties: { type: string } + + RuleMatch: + type: object + properties: + eventKinds: + type: array + items: { type: string } + minSeverity: { type: string, enum: [info, low, medium, high, critical] } + verdicts: + type: array + items: { type: string } + labels: + type: array + items: { type: string } + kevOnly: { type: boolean } + + RuleAction: + type: object + required: [actionId, channel] + properties: + actionId: { type: string } + channel: { type: string } + template: { type: string } + digest: { type: string, description: "Digest window key e.g. instant|5m|15m|1h|1d" } + throttle: { type: string, description: "ISO-8601 duration, e.g. PT5M" } + locale: { type: string } + enabled: { type: boolean, default: true } + metadata: + type: object + additionalProperties: { type: string } + + NotifyTemplate: + type: object + required: [templateId, tenantId, key, channelType, locale, body, renderMode, format] + properties: + templateId: { type: string } + tenantId: { type: string } + key: { type: string } + channelType: { type: string, enum: [slack, teams, email, webhook, custom] } + locale: { type: string, description: "BCP-47, lower-case" } + renderMode: { type: string, enum: [Markdown, Html, AdaptiveCard, PlainText, Json] } + format: { type: string, enum: [slack, teams, email, webhook, json] } + description: { type: string } + body: { type: string } + metadata: + type: object + additionalProperties: { type: string } + + Incident: + type: object + required: [incidentId, kind, severity, startedAt] + properties: + incidentId: { type: string } + kind: { type: string, description: "outage|degradation|security|ops-drill" } + severity: { type: string, enum: [minor, major, critical] } + startedAt: { type: string, format: date-time } + endedAt: { type: string, format: date-time } + shortDescription: { type: string } + description: { type: string } + metadata: + type: object + additionalProperties: { type: string } + + QuietHours: + type: object + required: [quietHoursId, windows] + properties: + quietHoursId: { type: string } + windows: + type: array + items: { $ref: '#/components/schemas/QuietHoursWindow' } + exemptions: + type: array + description: Event kinds that bypass quiet hours + items: + type: object + properties: + eventKinds: + type: array + items: { type: string } + reason: { type: string } + + QuietHoursWindow: + type: object + required: [timezone, days, start, end] + properties: + timezone: { type: string, description: "IANA TZ, e.g., UTC" } + days: + type: array + items: + type: string + enum: [Mon, Tue, Wed, Thu, Fri, Sat, Sun] + start: { type: string, description: "HH:mm" } + end: { type: string, description: "HH:mm" } diff --git a/src/Notifier/StellaOps.Notifier/TASKS.md b/src/Notifier/StellaOps.Notifier/TASKS.md new file mode 100644 index 000000000..2a139b01e --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/TASKS.md @@ -0,0 +1,15 @@ +# Sprint 171 · Notifier.I + +| ID | Status | Owner(s) | Notes | +| --- | --- | --- | --- | +| NOTIFY-ATTEST-74-001 | DONE (2025-11-16) | Notifications Service Guild | Attestation template suite complete; Slack expiry template added; coverage tests guard required channels. | +| NOTIFY-ATTEST-74-002 | TODO | Notifications Service Guild · KMS Guild | Wire notifications to key rotation/revocation events + transparency witness failures (depends on 74-001). | +| NOTIFY-OAS-61-001 | DONE (2025-11-17) | Notifications Service Guild · API Contracts Guild | OAS updated with rules/templates/incidents/quiet hours and standard error envelope. | +| NOTIFY-OAS-61-002 | DONE (2025-11-17) | Notifications Service Guild | `.well-known/openapi` discovery endpoint with scope metadata implemented. | +| NOTIFY-OAS-62-001 | DONE (2025-11-17) | Notifications Service Guild · SDK Generator Guild | SDK usage examples + smoke tests (depends on 61-002). | +| NOTIFY-OAS-63-001 | TODO | Notifications Service Guild · API Governance Guild | Deprecation headers + template notices for retiring APIs (depends on 62-001). | +| NOTIFY-OBS-51-001 | TODO | Notifications Service Guild · Observability Guild | Integrate SLO evaluator webhooks once schema lands. | +| NOTIFY-OBS-55-001 | TODO | Notifications Service Guild · Ops Guild | Incident mode start/stop notifications; quiet-hour overrides. | +| NOTIFY-RISK-66-001 | TODO | Notifications Service Guild · Risk Engine Guild | Trigger risk severity escalation/downgrade notifications (waiting on Policy export). | +| NOTIFY-RISK-67-001 | TODO | Notifications Service Guild · Policy Guild | Notify when risk profiles publish/deprecate/threshold-change (depends on 66-001). | +| NOTIFY-RISK-68-001 | TODO | Notifications Service Guild | Per-profile routing rules + quiet hours for risk alerts (depends on 67-001). | diff --git a/src/Notifier/StellaOps.Notifier/docs/NOTIFY-OAS-61-ETAG.md b/src/Notifier/StellaOps.Notifier/docs/NOTIFY-OAS-61-ETAG.md new file mode 100644 index 000000000..895e897b6 --- /dev/null +++ b/src/Notifier/StellaOps.Notifier/docs/NOTIFY-OAS-61-ETAG.md @@ -0,0 +1,15 @@ +# Notifier OAS Discovery — ETag Guidance + +The Notifier WebService exposes its OpenAPI document at `/.well-known/openapi` with headers: + +- `X-OpenAPI-Scope: notify` +- `ETag: ""` (stable per spec bytes) +- `Cache-Control: public, max-age=300` + +Usage notes: + +- SDK generators and CI smoke tests should re-use the `ETag` for conditional GETs (`If-None-Match`) to avoid redundant downloads. +- Mirror/Offline bundles should copy `openapi/notify-openapi.yaml` and retain the `ETag` alongside the file hash used in air-gap validation. +- When the spec changes, the SHA-256 and `ETag` change together; callers can detect breaking/non-breaking updates via the published changelog (source of truth in `docs/api/notify-openapi.yaml`). + +Applies to tasks: NOTIFY-OAS-61-001/61-002/63-001. diff --git a/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Documents/PackApprovalDocument.cs b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Documents/PackApprovalDocument.cs new file mode 100644 index 000000000..6d59959d6 --- /dev/null +++ b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Documents/PackApprovalDocument.cs @@ -0,0 +1,49 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace StellaOps.Notify.Storage.Mongo.Documents; + +public sealed class PackApprovalDocument +{ + [BsonId] + public ObjectId Id { get; init; } + + [BsonElement("tenantId")] + public required string TenantId { get; init; } + + [BsonElement("eventId")] + public required Guid EventId { get; init; } + + [BsonElement("packId")] + public required string PackId { get; init; } + + [BsonElement("kind")] + public required string Kind { get; init; } + + [BsonElement("decision")] + public required string Decision { get; init; } + + [BsonElement("actor")] + public required string Actor { get; init; } + + [BsonElement("issuedAt")] + public required DateTimeOffset IssuedAt { get; init; } + + [BsonElement("policyId")] + public string? PolicyId { get; init; } + + [BsonElement("policyVersion")] + public string? PolicyVersion { get; init; } + + [BsonElement("resumeToken")] + public string? ResumeToken { get; init; } + + [BsonElement("summary")] + public string? Summary { get; init; } + + [BsonElement("labels")] + public Dictionary? Labels { get; init; } + + [BsonElement("createdAt")] + public required DateTimeOffset CreatedAt { get; init; } +} diff --git a/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Migrations/EnsurePackApprovalsCollectionMigration.cs b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Migrations/EnsurePackApprovalsCollectionMigration.cs new file mode 100644 index 000000000..240e837c4 --- /dev/null +++ b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Migrations/EnsurePackApprovalsCollectionMigration.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.Logging; +using StellaOps.Notify.Storage.Mongo.Internal; + +namespace StellaOps.Notify.Storage.Mongo.Migrations; + +internal sealed class EnsurePackApprovalsCollectionMigration : INotifyMongoMigration +{ + private readonly ILogger _logger; + + public EnsurePackApprovalsCollectionMigration(ILogger logger) + => _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + public string Id => "20251117_pack_approvals_collection_v1"; + + public async ValueTask ExecuteAsync(NotifyMongoContext context, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(context); + + var target = context.Options.PackApprovalsCollection; + + var cursor = await context.Database + .ListCollectionNamesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + var existing = await cursor.ToListAsync(cancellationToken).ConfigureAwait(false); + if (existing.Contains(target, StringComparer.Ordinal)) + { + return; + } + + _logger.LogInformation("Creating pack approvals collection '{Collection}'.", target); + await context.Database.CreateCollectionAsync(target, cancellationToken: cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Migrations/EnsurePackApprovalsIndexesMigration.cs b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Migrations/EnsurePackApprovalsIndexesMigration.cs new file mode 100644 index 000000000..a08b228db --- /dev/null +++ b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Migrations/EnsurePackApprovalsIndexesMigration.cs @@ -0,0 +1,41 @@ +using MongoDB.Bson; +using MongoDB.Driver; +using StellaOps.Notify.Storage.Mongo.Internal; + +namespace StellaOps.Notify.Storage.Mongo.Migrations; + +internal sealed class EnsurePackApprovalsIndexesMigration : INotifyMongoMigration +{ + public string Id => "20251117_pack_approvals_indexes_v1"; + + public async ValueTask ExecuteAsync(NotifyMongoContext context, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(context); + + var collection = context.Database.GetCollection(context.Options.PackApprovalsCollection); + + var unique = new CreateIndexModel( + Builders.IndexKeys + .Ascending("tenantId") + .Ascending("packId") + .Ascending("eventId"), + new CreateIndexOptions + { + Name = "tenant_pack_event", + Unique = true + }); + + await collection.Indexes.CreateOneAsync(unique, cancellationToken: cancellationToken).ConfigureAwait(false); + + var issuedAt = new CreateIndexModel( + Builders.IndexKeys + .Ascending("tenantId") + .Descending("issuedAt"), + new CreateIndexOptions + { + Name = "tenant_issuedAt" + }); + + await collection.Indexes.CreateOneAsync(issuedAt, cancellationToken: cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Repositories/INotifyPackApprovalRepository.cs b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Repositories/INotifyPackApprovalRepository.cs new file mode 100644 index 000000000..8c16b564d --- /dev/null +++ b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Repositories/INotifyPackApprovalRepository.cs @@ -0,0 +1,8 @@ +using StellaOps.Notify.Storage.Mongo.Documents; + +namespace StellaOps.Notify.Storage.Mongo.Repositories; + +public interface INotifyPackApprovalRepository +{ + Task UpsertAsync(PackApprovalDocument document, CancellationToken cancellationToken = default); +} diff --git a/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Repositories/NotifyPackApprovalRepository.cs b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Repositories/NotifyPackApprovalRepository.cs new file mode 100644 index 000000000..a123392e5 --- /dev/null +++ b/src/Notify/__Libraries/StellaOps.Notify.Storage.Mongo/Repositories/NotifyPackApprovalRepository.cs @@ -0,0 +1,29 @@ +using MongoDB.Driver; +using StellaOps.Notify.Storage.Mongo.Documents; +using StellaOps.Notify.Storage.Mongo.Internal; + +namespace StellaOps.Notify.Storage.Mongo.Repositories; + +internal sealed class NotifyPackApprovalRepository : INotifyPackApprovalRepository +{ + private readonly IMongoCollection _collection; + + public NotifyPackApprovalRepository(NotifyMongoContext context) + { + ArgumentNullException.ThrowIfNull(context); + _collection = context.Database.GetCollection(context.Options.PackApprovalsCollection); + } + + public async Task UpsertAsync(PackApprovalDocument document, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(document); + + var filter = Builders.Filter.And( + Builders.Filter.Eq(x => x.TenantId, document.TenantId), + Builders.Filter.Eq(x => x.PackId, document.PackId), + Builders.Filter.Eq(x => x.EventId, document.EventId)); + + var options = new ReplaceOptions { IsUpsert = true }; + await _collection.ReplaceOneAsync(filter, document, options, cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Provenance/StellaOps.Provenance.Attestation.Tool/Directory.Build.props b/src/Provenance/StellaOps.Provenance.Attestation.Tool/Directory.Build.props new file mode 100644 index 000000000..a143391fb --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation.Tool/Directory.Build.props @@ -0,0 +1,5 @@ + + + ;; + + diff --git a/src/Provenance/StellaOps.Provenance.Attestation.Tool/Program.cs b/src/Provenance/StellaOps.Provenance.Attestation.Tool/Program.cs new file mode 100644 index 000000000..8876b26e2 --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation.Tool/Program.cs @@ -0,0 +1,60 @@ +using System.Text; +using System.Text.Json; +using StellaOps.Provenance.Attestation; + +static int PrintUsage() +{ + Console.Error.WriteLine("Usage: stella-forensic-verify --payload --signature-hex --key-hex [--key-id ] [--content-type ]"); + return 1; +} + +string? GetArg(string name) +{ + for (int i = 0; i < args.Length - 1; i++) + { + if (args[i].Equals(name, StringComparison.OrdinalIgnoreCase)) + return args[i + 1]; + } + return null; +} + +string? payloadPath = GetArg("--payload"); +string? signatureHex = GetArg("--signature-hex"); +string? keyHex = GetArg("--key-hex"); +string keyId = GetArg("--key-id") ?? "hmac"; +string contentType = GetArg("--content-type") ?? "application/octet-stream"; + +if (payloadPath is null || signatureHex is null || keyHex is null) +{ + return PrintUsage(); +} + +byte[] payload = await System.IO.File.ReadAllBytesAsync(payloadPath); +byte[] signature; +byte[] key; +try +{ + signature = Hex.FromHex(signatureHex); + key = Hex.FromHex(keyHex); +} +catch (Exception ex) +{ + Console.Error.WriteLine($"hex parse error: {ex.Message}"); + return 1; +} + +var request = new SignRequest(payload, contentType); +var signResult = new SignResult(signature, keyId, DateTimeOffset.MinValue, null); + +var verifier = new HmacVerifier(new InMemoryKeyProvider(keyId, key)); +var result = await verifier.VerifyAsync(request, signResult); + +var json = JsonSerializer.Serialize(new +{ + valid = result.IsValid, + reason = result.Reason, + verifiedAt = result.VerifiedAt.ToUniversalTime().ToString("O") +}); +Console.WriteLine(json); + +return result.IsValid ? 0 : 2; diff --git a/src/Provenance/StellaOps.Provenance.Attestation.Tool/StellaOps.Provenance.Attestation.Tool.csproj b/src/Provenance/StellaOps.Provenance.Attestation.Tool/StellaOps.Provenance.Attestation.Tool.csproj new file mode 100644 index 000000000..6f81e5700 --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation.Tool/StellaOps.Provenance.Attestation.Tool.csproj @@ -0,0 +1,14 @@ + + + net10.0 + preview + enable + enable + true + stella-forensic-verify + ../../out/tools + + + + + diff --git a/src/Provenance/StellaOps.Provenance.Attestation.Tool/tmpfile.txt b/src/Provenance/StellaOps.Provenance.Attestation.Tool/tmpfile.txt new file mode 100644 index 000000000..9daeafb98 --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation.Tool/tmpfile.txt @@ -0,0 +1 @@ +test diff --git a/src/Provenance/StellaOps.Provenance.Attestation/BuildModels.cs b/src/Provenance/StellaOps.Provenance.Attestation/BuildModels.cs new file mode 100644 index 000000000..7a3a93d7b --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation/BuildModels.cs @@ -0,0 +1,113 @@ +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Linq; + +namespace StellaOps.Provenance.Attestation; + +public sealed record BuildDefinition( + string BuildType, + IReadOnlyDictionary? ExternalParameters = null, + IReadOnlyDictionary? ResolvedDependencies = null); + +public sealed record BuildMetadata( + string? BuildInvocationId, + DateTimeOffset? BuildStartedOn, + DateTimeOffset? BuildFinishedOn, + bool? Reproducible = null, + IReadOnlyDictionary? Completeness = null, + IReadOnlyDictionary? Environment = null); + +public static class CanonicalJson +{ + private static readonly JsonSerializerOptions Options = new() + { + PropertyNamingPolicy = null, + WriteIndented = false, + DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull + }; + + public static byte[] SerializeToUtf8Bytes(T value) + { + var element = JsonSerializer.SerializeToElement(value, Options); + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = false }); + WriteCanonical(element, writer); + writer.Flush(); + return stream.ToArray(); + } + + public static string SerializeToString(T value) => Encoding.UTF8.GetString(SerializeToUtf8Bytes(value)); + + private static void WriteCanonical(JsonElement element, Utf8JsonWriter writer) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + writer.WriteStartObject(); + foreach (var property in element.EnumerateObject().OrderBy(p => p.Name, StringComparer.Ordinal)) + { + writer.WritePropertyName(property.Name); + WriteCanonical(property.Value, writer); + } + writer.WriteEndObject(); + break; + case JsonValueKind.Array: + writer.WriteStartArray(); + foreach (var item in element.EnumerateArray()) + { + WriteCanonical(item, writer); + } + writer.WriteEndArray(); + break; + default: + element.WriteTo(writer); + break; + } + } +} + +public static class MerkleTree +{ + public static byte[] ComputeRoot(IEnumerable leaves) + { + var leafList = leaves?.ToList() ?? throw new ArgumentNullException(nameof(leaves)); + if (leafList.Count == 0) throw new ArgumentException("At least one leaf required", nameof(leaves)); + + var level = leafList.Select(NormalizeLeaf).ToList(); + using var sha = SHA256.Create(); + + while (level.Count > 1) + { + var next = new List((level.Count + 1) / 2); + for (var i = 0; i < level.Count; i += 2) + { + var left = level[i]; + var right = i + 1 < level.Count ? level[i + 1] : left; + var combined = new byte[left.Length + right.Length]; + Buffer.BlockCopy(left, 0, combined, 0, left.Length); + Buffer.BlockCopy(right, 0, combined, left.Length, right.Length); + next.Add(sha.ComputeHash(combined)); + } + level = next; + } + + return level[0]; + + static byte[] NormalizeLeaf(byte[] data) + { + if (data.Length == 32) return data; + using var sha = SHA256.Create(); + return sha.ComputeHash(data); + } + } +} + +public sealed record BuildStatement( + BuildDefinition BuildDefinition, + BuildMetadata BuildMetadata); + +public static class BuildStatementFactory +{ + public static BuildStatement Create(BuildDefinition definition, BuildMetadata metadata) => new(definition, metadata); +} diff --git a/src/Provenance/StellaOps.Provenance.Attestation/Hex.cs b/src/Provenance/StellaOps.Provenance.Attestation/Hex.cs new file mode 100644 index 000000000..ac08a4e77 --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation/Hex.cs @@ -0,0 +1,20 @@ +using System.Globalization; + +namespace StellaOps.Provenance.Attestation; + +public static class Hex +{ + public static byte[] FromHex(string hex) + { + if (string.IsNullOrWhiteSpace(hex)) throw new ArgumentException("hex is required", nameof(hex)); + if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) hex = hex[2..]; + if (hex.Length % 2 != 0) throw new FormatException("hex length must be even"); + + var bytes = new byte[hex.Length / 2]; + for (int i = 0; i < bytes.Length; i++) + { + bytes[i] = byte.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + } + return bytes; + } +} diff --git a/src/Provenance/StellaOps.Provenance.Attestation/PromotionAttestation.cs b/src/Provenance/StellaOps.Provenance.Attestation/PromotionAttestation.cs new file mode 100644 index 000000000..46e29f5df --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation/PromotionAttestation.cs @@ -0,0 +1,21 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace StellaOps.Provenance.Attestation; + +public sealed record PromotionPredicate( + string ImageDigest, + string SbomDigest, + string VexDigest, + string PromotionId, + string? RekorEntry = null, + IReadOnlyDictionary? Metadata = null); + +public static class PromotionAttestationBuilder +{ + public static byte[] CreateCanonicalJson(PromotionPredicate predicate) + { + if (predicate is null) throw new ArgumentNullException(nameof(predicate)); + return CanonicalJson.SerializeToUtf8Bytes(predicate); + } +} diff --git a/src/Provenance/StellaOps.Provenance.Attestation/Signers.cs b/src/Provenance/StellaOps.Provenance.Attestation/Signers.cs new file mode 100644 index 000000000..60f57c3f7 --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation/Signers.cs @@ -0,0 +1,107 @@ +using System.Security.Cryptography; + +namespace StellaOps.Provenance.Attestation; + +public sealed record SignRequest( + byte[] Payload, + string ContentType, + IReadOnlyDictionary? Claims = null, + IReadOnlyCollection? RequiredClaims = null); + +public sealed record SignResult( + byte[] Signature, + string KeyId, + DateTimeOffset SignedAt, + IReadOnlyDictionary? Claims); + +public interface IKeyProvider +{ + string KeyId { get; } + byte[] KeyMaterial { get; } +} + +public interface IAuditSink +{ + void LogSigned(string keyId, string contentType, IReadOnlyDictionary? claims, DateTimeOffset signedAt); + void LogMissingClaim(string keyId, string claimName); +} + +public sealed class NullAuditSink : IAuditSink +{ + public static readonly NullAuditSink Instance = new(); + private NullAuditSink() { } + public void LogSigned(string keyId, string contentType, IReadOnlyDictionary? claims, DateTimeOffset signedAt) { } + public void LogMissingClaim(string keyId, string claimName) { } +} + +public sealed class HmacSigner : ISigner +{ + private readonly IKeyProvider _keyProvider; + private readonly IAuditSink _audit; + private readonly TimeProvider _timeProvider; + + public HmacSigner(IKeyProvider keyProvider, IAuditSink? audit = null, TimeProvider? timeProvider = null) + { + _keyProvider = keyProvider ?? throw new ArgumentNullException(nameof(keyProvider)); + _audit = audit ?? NullAuditSink.Instance; + _timeProvider = timeProvider ?? TimeProvider.System; + } + + public Task SignAsync(SignRequest request, CancellationToken cancellationToken = default) + { + if (request is null) throw new ArgumentNullException(nameof(request)); + + if (request.RequiredClaims is not null) + { + foreach (var required in request.RequiredClaims) + { + if (request.Claims is null || !request.Claims.ContainsKey(required)) + { + _audit.LogMissingClaim(_keyProvider.KeyId, required); + throw new InvalidOperationException($"Missing required claim {required}."); + } + } + } + + using var hmac = new HMACSHA256(_keyProvider.KeyMaterial); + var signature = hmac.ComputeHash(request.Payload); + var signedAt = _timeProvider.GetUtcNow(); + + _audit.LogSigned(_keyProvider.KeyId, request.ContentType, request.Claims, signedAt); + + return Task.FromResult(new SignResult( + Signature: signature, + KeyId: _keyProvider.KeyId, + SignedAt: signedAt, + Claims: request.Claims)); + } +} + +public interface ISigner +{ + Task SignAsync(SignRequest request, CancellationToken cancellationToken = default); +} + +public sealed class InMemoryKeyProvider : IKeyProvider +{ + public string KeyId { get; } + public byte[] KeyMaterial { get; } + + public InMemoryKeyProvider(string keyId, byte[] keyMaterial) + { + KeyId = keyId ?? throw new ArgumentNullException(nameof(keyId)); + KeyMaterial = keyMaterial ?? throw new ArgumentNullException(nameof(keyMaterial)); + } +} + +public sealed class InMemoryAuditSink : IAuditSink +{ + public List<(string keyId, string contentType, IReadOnlyDictionary? claims, DateTimeOffset signedAt)> Signed { get; } = new(); + public List<(string keyId, string claim)> Missing { get; } = new(); + + public void LogSigned(string keyId, string contentType, IReadOnlyDictionary? claims, DateTimeOffset signedAt) + => Signed.Add((keyId, contentType, claims, signedAt)); + + public void LogMissingClaim(string keyId, string claimName) + => Missing.Add((keyId, claimName)); +} diff --git a/src/Provenance/StellaOps.Provenance.Attestation/StellaOps.Provenance.Attestation.csproj b/src/Provenance/StellaOps.Provenance.Attestation/StellaOps.Provenance.Attestation.csproj new file mode 100644 index 000000000..ecc3af66e --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation/StellaOps.Provenance.Attestation.csproj @@ -0,0 +1,9 @@ + + + net10.0 + preview + enable + enable + true + + diff --git a/src/Provenance/StellaOps.Provenance.Attestation/Verification.cs b/src/Provenance/StellaOps.Provenance.Attestation/Verification.cs new file mode 100644 index 000000000..7d3bc41b1 --- /dev/null +++ b/src/Provenance/StellaOps.Provenance.Attestation/Verification.cs @@ -0,0 +1,35 @@ +using System.Security.Cryptography; + +namespace StellaOps.Provenance.Attestation; + +public sealed record VerificationResult(bool IsValid, string Reason, DateTimeOffset VerifiedAt); + +public interface IVerifier +{ + Task VerifyAsync(SignRequest request, SignResult signature, CancellationToken cancellationToken = default); +} + +public sealed class HmacVerifier : IVerifier +{ + private readonly IKeyProvider _keyProvider; + private readonly TimeProvider _timeProvider; + + public HmacVerifier(IKeyProvider keyProvider, TimeProvider? timeProvider = null) + { + _keyProvider = keyProvider ?? throw new ArgumentNullException(nameof(keyProvider)); + _timeProvider = timeProvider ?? TimeProvider.System; + } + + public Task VerifyAsync(SignRequest request, SignResult signature, CancellationToken cancellationToken = default) + { + if (request is null) throw new ArgumentNullException(nameof(request)); + if (signature is null) throw new ArgumentNullException(nameof(signature)); + + using var hmac = new HMACSHA256(_keyProvider.KeyMaterial); + var expected = hmac.ComputeHash(request.Payload); + var ok = CryptographicOperations.FixedTimeEquals(expected, signature.Signature) && + string.Equals(_keyProvider.KeyId, signature.KeyId, StringComparison.Ordinal); + + var result = new VerificationResult( + IsValid: ok, + Reason: ok ? ok : signature diff --git a/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/CanonicalJsonTests.cs b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/CanonicalJsonTests.cs new file mode 100644 index 000000000..d9d37ffee --- /dev/null +++ b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/CanonicalJsonTests.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using FluentAssertions; +using StellaOps.Provenance.Attestation; +using Xunit; + +namespace StellaOps.Provenance.Attestation.Tests; + +public class CanonicalJsonTests +{ + [Fact] + public void Canonicalizes_property_order_and_omits_nulls() + { + var model = new BuildDefinition( + BuildType: "https://slsa.dev/provenance/v1", + ExternalParameters: new Dictionary + { + ["b"] = "2", + ["a"] = "1", + ["c"] = "3" + }, + ResolvedDependencies: null); + + var json = CanonicalJson.SerializeToString(model); + + json.Should().Be("{\"BuildType\":\"https://slsa.dev/provenance/v1\",\"ExternalParameters\":{\"a\":\"1\",\"b\":\"2\",\"c\":\"3\"}}"); + } +} diff --git a/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/HexTests.cs b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/HexTests.cs new file mode 100644 index 000000000..65b0a353d --- /dev/null +++ b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/HexTests.cs @@ -0,0 +1,21 @@ +using FluentAssertions; +using StellaOps.Provenance.Attestation; +using Xunit; + +namespace StellaOps.Provenance.Attestation.Tests; + +public class HexTests +{ + [Fact] + public void Parses_even_length_hex() + { + Hex.FromHex("0A0b").Should().BeEquivalentTo(new byte[] { 0x0A, 0x0B }); + } + + [Fact] + public void Throws_on_odd_length() + { + Action act = () => Hex.FromHex("ABC"); + act.Should().Throw(); + } +} diff --git a/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/MerkleTreeTests.cs b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/MerkleTreeTests.cs new file mode 100644 index 000000000..b5879662b --- /dev/null +++ b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/MerkleTreeTests.cs @@ -0,0 +1,38 @@ +using System.Security.Cryptography; +using System.Text; +using FluentAssertions; +using StellaOps.Provenance.Attestation; +using Xunit; + +namespace StellaOps.Provenance.Attestation.Tests; + +public class MerkleTreeTests +{ + [Fact] + public void Computes_deterministic_root_for_same_inputs() + { + var leaves = new[] + { + Encoding.UTF8.GetBytes("a"), + Encoding.UTF8.GetBytes("b"), + Encoding.UTF8.GetBytes("c") + }; + + var root1 = MerkleTree.ComputeRoot(leaves); + var root2 = MerkleTree.ComputeRoot(leaves); + + root1.Should().BeEquivalentTo(root2); + } + + [Fact] + public void Normalizes_non_hash_leaves() + { + var leaves = new[] { Encoding.UTF8.GetBytes("single") }; + var root = MerkleTree.ComputeRoot(leaves); + + using var sha = SHA256.Create(); + var expected = sha.ComputeHash(leaves[0]); + + root.Should().BeEquivalentTo(expected); + } +} diff --git a/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/PromotionAttestationBuilderTests.cs b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/PromotionAttestationBuilderTests.cs new file mode 100644 index 000000000..9d329f8bd --- /dev/null +++ b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/PromotionAttestationBuilderTests.cs @@ -0,0 +1,26 @@ +using System.Text; +using FluentAssertions; +using StellaOps.Provenance.Attestation; +using Xunit; + +namespace StellaOps.Provenance.Attestation.Tests; + +public class PromotionAttestationBuilderTests +{ + [Fact] + public void Produces_canonical_json_for_predicate() + { + var predicate = new PromotionPredicate( + ImageDigest: sha256:img, + SbomDigest: sha256:sbom, + VexDigest: sha256:vex, + PromotionId: prom-1, + RekorEntry: uuid, + Metadata: new Dictionary{{env,prod}}); + + var bytes = PromotionAttestationBuilder.CreateCanonicalJson(predicate); + var json = Encoding.UTF8.GetString(bytes); + + json.Should().Be("ImageDigest":"sha256:img"); + } +} diff --git a/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/SignerTests.cs b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/SignerTests.cs new file mode 100644 index 000000000..9faa2596f --- /dev/null +++ b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/SignerTests.cs @@ -0,0 +1,47 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using System.Collections.Generic; +using FluentAssertions; +using StellaOps.Provenance.Attestation; +using Xunit; + +namespace StellaOps.Provenance.Attestation.Tests; + +public class SignerTests +{ + [Fact] + public async Task HmacSigner_is_deterministic_for_same_input() + { + var key = new InMemoryKeyProvider("test-key", Encoding.UTF8.GetBytes("secret")); + var audit = new InMemoryAuditSink(); + var signer = new HmacSigner(key, audit, TimeProvider.System); + + var request = new SignRequest(Encoding.UTF8.GetBytes("payload"), "application/json"); + + var r1 = await signer.SignAsync(request); + var r2 = await signer.SignAsync(request); + + r1.Signature.Should().BeEquivalentTo(r2.Signature); + r1.KeyId.Should().Be("test-key"); + audit.Signed.Should().HaveCount(2); + } + + [Fact] + public async Task HmacSigner_enforces_required_claims() + { + var key = new InMemoryKeyProvider("test-key", Encoding.UTF8.GetBytes("secret")); + var audit = new InMemoryAuditSink(); + var signer = new HmacSigner(key, audit, TimeProvider.System); + + var request = new SignRequest( + Payload: Encoding.UTF8.GetBytes("payload"), + ContentType: "application/json", + Claims: new Dictionary { ["foo"] = "bar" }, + RequiredClaims: new[] { "foo", "bar" }); + + var ex = await Assert.ThrowsAsync(() => signer.SignAsync(request)); + ex.Message.Should().Contain("bar"); + audit.Missing.Should().ContainSingle(m => m.claim == "bar"); + } +} diff --git a/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/StellaOps.Provenance.Attestation.Tests.csproj b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/StellaOps.Provenance.Attestation.Tests.csproj new file mode 100644 index 000000000..7a97f3124 --- /dev/null +++ b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/StellaOps.Provenance.Attestation.Tests.csproj @@ -0,0 +1,14 @@ + + + net10.0 + false + enable + preview + + + + + + + + diff --git a/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/VerificationTests.cs b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/VerificationTests.cs new file mode 100644 index 000000000..800a414b7 --- /dev/null +++ b/src/Provenance/__Tests/StellaOps.Provenance.Attestation.Tests/VerificationTests.cs @@ -0,0 +1,42 @@ +using System.Text; +using FluentAssertions; +using StellaOps.Provenance.Attestation; +using Xunit; + +namespace StellaOps.Provenance.Attestation.Tests; + +public class VerificationTests +{ + [Fact] + public async Task Verifier_accepts_valid_signature() + { + var key = new InMemoryKeyProvider(test-key, Encoding.UTF8.GetBytes(secret)); + var signer = new HmacSigner(key); + var verifier = new HmacVerifier(key); + + var request = new SignRequest(Encoding.UTF8.GetBytes(payload), application/json); + var signature = await signer.SignAsync(request); + + var result = await verifier.VerifyAsync(request, signature); + result.IsValid.Should().BeTrue(); + result.Reason.Should().Be(ok); + } + + [Fact] + public async Task Verifier_rejects_tampered_payload() + { + var key = new InMemoryKeyProvider(test-key, Encoding.UTF8.GetBytes(secret)); + var signer = new HmacSigner(key); + var verifier = new HmacVerifier(key); + + var request = new SignRequest(Encoding.UTF8.GetBytes(payload), application/json); + var signature = await signer.SignAsync(request); + + var tampered = new SignRequest(Encoding.UTF8.GetBytes(payload-tampered), application/json); + var result = await verifier.VerifyAsync(tampered, signature); + + result.IsValid.Should().BeFalse(); + result.Reason.Should().Contain(mismatch); + } +} +EOF} diff --git a/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/Internal/DotNetEntrypointResolver.cs b/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/Internal/DotNetEntrypointResolver.cs new file mode 100644 index 000000000..a6cd6fbb9 --- /dev/null +++ b/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/Internal/DotNetEntrypointResolver.cs @@ -0,0 +1,215 @@ +using System.Text.Json; + +namespace StellaOps.Scanner.Analyzers.Lang.DotNet.Internal; + +/// +/// Resolves publish artifacts (deps/runtimeconfig) into deterministic entrypoint identities. +/// +public static class DotNetEntrypointResolver +{ + private static readonly EnumerationOptions Enumeration = new() + { + RecurseSubdirectories = true, + IgnoreInaccessible = true, + AttributesToSkip = FileAttributes.Device | FileAttributes.ReparsePoint + }; + + public static ValueTask> ResolveAsync( + LanguageAnalyzerContext context, + CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(context); + + var depsFiles = Directory + .EnumerateFiles(context.RootPath, "*.deps.json", Enumeration) + .OrderBy(static path => path, StringComparer.Ordinal) + .ToArray(); + + if (depsFiles.Length == 0) + { + return ValueTask.FromResult>(Array.Empty()); + } + + var results = new List(depsFiles.Length); + + foreach (var depsPath in depsFiles) + { + cancellationToken.ThrowIfCancellationRequested(); + + try + { + var relativeDepsPath = NormalizeRelative(context.GetRelativePath(depsPath)); + var depsFile = DotNetDepsFile.Load(depsPath, relativeDepsPath, cancellationToken); + if (depsFile is null) + { + continue; + } + + DotNetRuntimeConfig? runtimeConfig = null; + var runtimeConfigPath = Path.ChangeExtension(depsPath, ".runtimeconfig.json"); + string? relativeRuntimeConfig = null; + + if (!string.IsNullOrEmpty(runtimeConfigPath) && File.Exists(runtimeConfigPath)) + { + relativeRuntimeConfig = NormalizeRelative(context.GetRelativePath(runtimeConfigPath)); + runtimeConfig = DotNetRuntimeConfig.Load(runtimeConfigPath, relativeRuntimeConfig, cancellationToken); + } + + var tfms = CollectTargetFrameworks(depsFile, runtimeConfig); + var rids = CollectRuntimeIdentifiers(depsFile, runtimeConfig); + var publishKind = DeterminePublishKind(depsFile); + + var name = GetEntrypointName(depsPath); + var id = BuildDeterministicId(name, tfms, rids, publishKind); + + results.Add(new DotNetEntrypoint( + Id: id, + Name: name, + TargetFrameworks: tfms, + RuntimeIdentifiers: rids, + RelativeDepsPath: relativeDepsPath, + RelativeRuntimeConfigPath: relativeRuntimeConfig, + PublishKind: publishKind)); + } + catch (IOException) + { + continue; + } + catch (JsonException) + { + continue; + } + catch (UnauthorizedAccessException) + { + continue; + } + } + + return ValueTask.FromResult>(results); + } + + private static string GetEntrypointName(string depsPath) + { + // Strip .json then any trailing .deps suffix to yield a logical entrypoint name. + var stem = Path.GetFileNameWithoutExtension(depsPath); // removes .json + + if (stem.EndsWith(".deps", StringComparison.OrdinalIgnoreCase)) + { + stem = stem[..^".deps".Length]; + } + + return stem; + } + + private static IReadOnlyCollection CollectTargetFrameworks(DotNetDepsFile depsFile, DotNetRuntimeConfig? runtimeConfig) + { + var tfms = new SortedSet(StringComparer.OrdinalIgnoreCase); + + foreach (var library in depsFile.Libraries.Values) + { + foreach (var tfm in library.TargetFrameworks) + { + tfms.Add(tfm); + } + } + + if (runtimeConfig is not null) + { + foreach (var tfm in runtimeConfig.Tfms) + { + tfms.Add(tfm); + } + + foreach (var framework in runtimeConfig.Frameworks) + { + tfms.Add(framework); + } + } + + return tfms; + } + + private static IReadOnlyCollection CollectRuntimeIdentifiers(DotNetDepsFile depsFile, DotNetRuntimeConfig? runtimeConfig) + { + var rids = new SortedSet(StringComparer.OrdinalIgnoreCase); + + foreach (var library in depsFile.Libraries.Values) + { + foreach (var rid in library.RuntimeIdentifiers) + { + rids.Add(rid); + } + } + + if (runtimeConfig is not null) + { + foreach (var entry in runtimeConfig.RuntimeGraph) + { + rids.Add(entry.Rid); + + foreach (var fallback in entry.Fallbacks) + { + if (!string.IsNullOrWhiteSpace(fallback)) + { + rids.Add(fallback); + } + } + } + } + + return rids; + } + + private static DotNetPublishKind DeterminePublishKind(DotNetDepsFile depsFile) + { + foreach (var library in depsFile.Libraries.Values) + { + if (library.Id.StartsWith("Microsoft.NETCore.App.Runtime.", StringComparison.OrdinalIgnoreCase) || + library.Id.StartsWith("Microsoft.WindowsDesktop.App.Runtime.", StringComparison.OrdinalIgnoreCase)) + { + return DotNetPublishKind.SelfContained; + } + } + + return DotNetPublishKind.FrameworkDependent; + } + + private static string BuildDeterministicId( + string name, + IReadOnlyCollection tfms, + IReadOnlyCollection rids, + DotNetPublishKind publishKind) + { + var tfmPart = tfms.Count == 0 ? "unknown" : string.Join('+', tfms.OrderBy(t => t, StringComparer.OrdinalIgnoreCase)); + var ridPart = rids.Count == 0 ? "none" : string.Join('+', rids.OrderBy(r => r, StringComparer.OrdinalIgnoreCase)); + var publishPart = publishKind.ToString().ToLowerInvariant(); + return $"{name}:{tfmPart}:{ridPart}:{publishPart}"; + } + + private static string NormalizeRelative(string path) + { + if (string.IsNullOrWhiteSpace(path) || path == ".") + { + return "."; + } + + var normalized = path.Replace('\\', '/'); + return string.IsNullOrWhiteSpace(normalized) ? "." : normalized; + } +} + +public sealed record DotNetEntrypoint( + string Id, + string Name, + IReadOnlyCollection TargetFrameworks, + IReadOnlyCollection RuntimeIdentifiers, + string RelativeDepsPath, + string? RelativeRuntimeConfigPath, + DotNetPublishKind PublishKind); + +public enum DotNetPublishKind +{ + Unknown = 0, + FrameworkDependent = 1, + SelfContained = 2 +} diff --git a/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/AnalysisSnapshot.cs b/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/AnalysisSnapshot.cs new file mode 100644 index 000000000..f95514ae9 --- /dev/null +++ b/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/AnalysisSnapshot.cs @@ -0,0 +1,7 @@ +namespace StellaOps.Scanner.Analyzers.Lang; + +public sealed class AnalysisSnapshot +{ + public IReadOnlyList Entrypoints { get; set; } = Array.Empty(); + public IReadOnlyList Components { get; set; } = Array.Empty(); +} diff --git a/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/LanguageComponentEvidenceExtensions.cs b/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/LanguageComponentEvidenceExtensions.cs new file mode 100644 index 000000000..2271ed928 --- /dev/null +++ b/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/LanguageComponentEvidenceExtensions.cs @@ -0,0 +1,15 @@ +using System; + +namespace StellaOps.Scanner.Analyzers.Lang; + +internal static class LanguageComponentEvidenceExtensions +{ + /// + /// Builds a stable key for evidence items to support deterministic dictionaries. + /// + public static string ToKey(this LanguageComponentEvidence evidence) + { + ArgumentNullException.ThrowIfNull(evidence); + return $"{evidence.Kind}:{evidence.Source}:{evidence.Locator}".ToLowerInvariant(); + } +} diff --git a/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/LanguageEntrypointRecord.cs b/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/LanguageEntrypointRecord.cs new file mode 100644 index 000000000..aa938b838 --- /dev/null +++ b/src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/Core/LanguageEntrypointRecord.cs @@ -0,0 +1,96 @@ +using System.Text.Json.Serialization; + +namespace StellaOps.Scanner.Analyzers.Lang; + +public sealed class LanguageEntrypointRecord +{ + private readonly SortedDictionary _metadata; + private readonly SortedDictionary _evidence; + + public LanguageEntrypointRecord( + string id, + string name, + IEnumerable>? metadata = null, + IEnumerable? evidence = null) + { + if (string.IsNullOrWhiteSpace(id)) + { + throw new ArgumentException("Entrypoint id is required", nameof(id)); + } + + Id = id.Trim(); + Name = string.IsNullOrWhiteSpace(name) ? Id : name.Trim(); + + _metadata = new SortedDictionary(StringComparer.Ordinal); + foreach (var pair in metadata ?? Array.Empty>()) + { + _metadata[pair.Key] = pair.Value; + } + + _evidence = new SortedDictionary(StringComparer.Ordinal); + foreach (var item in evidence ?? Array.Empty()) + { + var key = item.ToKey(); + _evidence[key] = item; + } + } + + public string Id { get; } + + public string Name { get; } + + public IReadOnlyDictionary Metadata => _metadata; + + public IReadOnlyCollection Evidence => _evidence.Values; + + internal LanguageEntrypointSnapshot ToSnapshot() + => new() + { + Id = Id, + Name = Name, + Metadata = _metadata.ToDictionary(static kvp => kvp.Key, static kvp => kvp.Value, StringComparer.Ordinal), + Evidence = _evidence.Values + .Select(static item => new LanguageComponentEvidenceSnapshot + { + Kind = item.Kind, + Source = item.Source, + Locator = item.Locator, + Value = item.Value, + Sha256 = item.Sha256 + }) + .ToList() + }; + + internal static LanguageEntrypointRecord FromSnapshot(LanguageEntrypointSnapshot snapshot) + { + if (snapshot is null) + { + throw new ArgumentNullException(nameof(snapshot)); + } + + var evidence = snapshot.Evidence? + .Select(static e => new LanguageComponentEvidence(e.Kind, e.Source, e.Locator, e.Value, e.Sha256)) + .ToArray(); + + return new LanguageEntrypointRecord( + snapshot.Id ?? string.Empty, + snapshot.Name ?? snapshot.Id ?? string.Empty, + snapshot.Metadata, + evidence); + } +} + +public sealed class LanguageEntrypointSnapshot +{ + [JsonPropertyName("id")] + public string? Id { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("metadata")] + public Dictionary Metadata { get; set; } = new(StringComparer.Ordinal); + + [JsonPropertyName("evidence")] + public List Evidence { get; set; } = new(); +} diff --git a/src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace/TASKS.md b/src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace/TASKS.md new file mode 100644 index 000000000..8aba17573 --- /dev/null +++ b/src/Scanner/__Libraries/StellaOps.Scanner.EntryTrace/TASKS.md @@ -0,0 +1,5 @@ +# EntryTrace Tasks + +| Task ID | Status | Date | Summary | +| --- | --- | --- | --- | +| SCANNER-ENG-0008 | DONE | 2025-11-16 | Documented quarterly EntryTrace heuristic cadence and workflow; attached to Sprint 0138 Execution Log. | diff --git a/src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Tests/DotNet/DotNetEntrypointResolverTests.cs b/src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Tests/DotNet/DotNetEntrypointResolverTests.cs new file mode 100644 index 000000000..66f42cb44 --- /dev/null +++ b/src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Tests/DotNet/DotNetEntrypointResolverTests.cs @@ -0,0 +1,51 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using StellaOps.Scanner.Analyzers.Lang; +using StellaOps.Scanner.Analyzers.Lang.DotNet.Internal; +using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities; + +namespace StellaOps.Scanner.Analyzers.Lang.Tests.DotNet; + +public sealed class DotNetEntrypointResolverTests +{ + [Fact] + public async Task SimpleFixtureResolvesSingleEntrypointAsync() + { + var cancellationToken = TestContext.Current.CancellationToken; + var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "simple"); + + var context = new LanguageAnalyzerContext(fixturePath, TimeProvider.System); + var entrypoints = await DotNetEntrypointResolver.ResolveAsync(context, cancellationToken); + + Assert.Single(entrypoints); + + var entrypoint = entrypoints[0]; + Assert.Equal("Sample.App", entrypoint.Name); + Assert.Equal("Sample.App:Microsoft.AspNetCore.App@10.0.0+Microsoft.NETCore.App@10.0.0+net10.0:any+linux+linux-x64+unix+win+win-x86:frameworkdependent", entrypoint.Id); + Assert.Contains("net10.0", entrypoint.TargetFrameworks); + Assert.Contains("linux-x64", entrypoint.RuntimeIdentifiers); + Assert.Equal("Sample.App.deps.json", entrypoint.RelativeDepsPath); + Assert.Equal("Sample.App.runtimeconfig.json", entrypoint.RelativeRuntimeConfigPath); + Assert.Equal(DotNetPublishKind.FrameworkDependent, entrypoint.PublishKind); + } + + [Fact] + public async Task DeterministicOrderingIsStableAsync() + { + var cancellationToken = TestContext.Current.CancellationToken; + var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "multi"); + + var context = new LanguageAnalyzerContext(fixturePath, TimeProvider.System); + var first = await DotNetEntrypointResolver.ResolveAsync(context, cancellationToken); + var second = await DotNetEntrypointResolver.ResolveAsync(context, cancellationToken); + + Assert.Equal(first.Count, second.Count); + for (var i = 0; i < first.Count; i++) + { + Assert.Equal(first[i].Id, second[i].Id); + Assert.True(first[i].TargetFrameworks.SequenceEqual(second[i].TargetFrameworks)); + Assert.True(first[i].RuntimeIdentifiers.SequenceEqual(second[i].RuntimeIdentifiers)); + } + } +} diff --git a/src/Scheduler/__Libraries/StellaOps.Scheduler.Worker/TASKS.md b/src/Scheduler/__Libraries/StellaOps.Scheduler.Worker/TASKS.md new file mode 100644 index 000000000..00038ad49 --- /dev/null +++ b/src/Scheduler/__Libraries/StellaOps.Scheduler.Worker/TASKS.md @@ -0,0 +1,9 @@ +# Active Tasks + +| ID | Status | Owner(s) | Depends on | Description | Notes | +|----|--------|----------|------------|-------------|-------| +| SCHED-WORKER-23-101 | BLOCKED (2025-11-17) | Scheduler Worker Guild | SCHED-WORKER-21-203 | Implement policy re-evaluation worker that shards assets, honours rate limits, and updates progress for Console after policy activation events. | Waiting on Policy guild contract for activation event shape and throttle source. | +| SCHED-WORKER-15-401 | DONE (2025-11-17) | Scheduler Worker Guild | — | Investigate and stabilize PlannerBackgroundService fairness tests (tenant fairness cap; manual/event trigger priority). | Increased monotonic wait tolerance; tests now stable. | +| SCHED-WORKER-99-901 | DONE (2025-11-17) | Scheduler Worker Guild | — | Harden PolicyRunTargetingService coverage for incremental delta rules (MaxSboms, selector replay). | Added focused unit tests + deterministic stubs. | +| SCHED-SURFACE-01 | BLOCKED (2025-11-17) | Scheduler Worker Guild | — | Evaluate Surface.FS pointers when planning delta scans to avoid redundant work and prioritise drift-triggered assets. | Blocked: Surface.FS pointer schema/data source not documented; need contract from Surface/Policy guild. | +| SCHED-WORKER-99-902 | DONE (2025-11-17) | Scheduler Worker Guild | — | Housekeeping: add guard test to PolicyRunDispatchBackgroundService to ensure disabled policy mode performs no leases. | Added stub repository & clients; verified no lease attempts when disabled. | diff --git a/src/Scheduler/__Tests/StellaOps.Scheduler.Worker.Tests/PolicyRunDispatchBackgroundServiceTests.cs b/src/Scheduler/__Tests/StellaOps.Scheduler.Worker.Tests/PolicyRunDispatchBackgroundServiceTests.cs new file mode 100644 index 000000000..7821cacda --- /dev/null +++ b/src/Scheduler/__Tests/StellaOps.Scheduler.Worker.Tests/PolicyRunDispatchBackgroundServiceTests.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using StellaOps.Scheduler.Models; +using StellaOps.Scheduler.Storage.Mongo.Repositories; +using StellaOps.Scheduler.Worker.Options; +using StellaOps.Scheduler.Worker.Policy; + +namespace StellaOps.Scheduler.Worker.Tests; + +public sealed class PolicyRunDispatchBackgroundServiceTests +{ + [Fact] + public async Task ExecuteAsync_DoesNotLease_WhenPolicyDispatchDisabled() + { + var repository = new RecordingPolicyRunJobRepository(); + var options = CreateOptions(policyEnabled: false, idleDelay: TimeSpan.FromMilliseconds(1)); + var service = CreateService(repository, options); + + using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(50)); + + await service.StartAsync(cts.Token); + await service.StopAsync(CancellationToken.None); + + Assert.Equal(0, repository.LeaseAttempts); + } + + private static PolicyRunDispatchBackgroundService CreateService( + RecordingPolicyRunJobRepository repository, + SchedulerWorkerOptions options) + { + var executionService = new PolicyRunExecutionService( + repository, + new StubPolicyRunClient(), + Options.Create(options), + timeProvider: null, + new SchedulerWorkerMetrics(), + new StubPolicyRunTargetingService(), + new StubPolicySimulationWebhookClient(), + NullLogger.Instance); + + return new PolicyRunDispatchBackgroundService( + repository, + executionService, + Options.Create(options), + timeProvider: null, + NullLogger.Instance); + } + + private static SchedulerWorkerOptions CreateOptions(bool policyEnabled, TimeSpan idleDelay) + { + var options = new SchedulerWorkerOptions(); + options.Policy.Enabled = policyEnabled; + options.Policy.Dispatch.IdleDelay = idleDelay; + options.Policy.Dispatch.BatchSize = 1; + return options; + } + + private sealed class RecordingPolicyRunJobRepository : IPolicyRunJobRepository + { + public int LeaseAttempts { get; private set; } + + public Task InsertAsync(PolicyRunJob job, IClientSessionHandle? session = null, CancellationToken cancellationToken = default) + => Task.CompletedTask; + + public Task GetAsync(string tenantId, string jobId, IClientSessionHandle? session = null, CancellationToken cancellationToken = default) + => Task.FromResult(null); + + public Task GetByRunIdAsync(string tenantId, string runId, IClientSessionHandle? session = null, CancellationToken cancellationToken = default) + => Task.FromResult(null); + + public Task LeaseAsync( + string leaseOwner, + DateTimeOffset now, + TimeSpan leaseDuration, + int maxAttempts, + IClientSessionHandle? session = null, + CancellationToken cancellationToken = default) + { + Interlocked.Increment(ref LeaseAttempts); + return Task.FromResult(null); + } + + public Task> ListAsync( + string tenantId, + string? policyId = null, + PolicyRunMode? mode = null, + IReadOnlyCollection? statuses = null, + DateTimeOffset? queuedAfter = null, + int limit = 50, + IClientSessionHandle? session = null, + CancellationToken cancellationToken = default) + => Task.FromResult>(Array.Empty()); + + public Task ReplaceAsync( + PolicyRunJob job, + string? expectedLeaseOwner = null, + IClientSessionHandle? session = null, + CancellationToken cancellationToken = default) + => Task.FromResult(true); + + public Task CountAsync( + string tenantId, + PolicyRunMode mode, + IReadOnlyCollection statuses, + CancellationToken cancellationToken = default) + => Task.FromResult(0L); + } + + private sealed class StubPolicyRunClient : IPolicyRunClient + { + public Task SubmitAsync(PolicyRunJob job, PolicyRunRequest request, CancellationToken cancellationToken) + => Task.FromResult(PolicyRunSubmissionResult.Failed("disabled")); + } + + private sealed class StubPolicyRunTargetingService : IPolicyRunTargetingService + { + public Task EnsureTargetsAsync(PolicyRunJob job, CancellationToken cancellationToken) + => Task.FromResult(PolicyRunTargetingResult.Unchanged(job)); + } + + private sealed class StubPolicySimulationWebhookClient : IPolicySimulationWebhookClient + { + public Task NotifyAsync(PolicySimulationWebhookPayload payload, CancellationToken cancellationToken) + => Task.CompletedTask; + } +} diff --git a/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/MongoIndexModelTests.cs b/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/MongoIndexModelTests.cs new file mode 100644 index 000000000..cd3da7e4d --- /dev/null +++ b/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/MongoIndexModelTests.cs @@ -0,0 +1,62 @@ +using MongoDB.Driver; +using StellaOps.TaskRunner.Infrastructure.Execution; +using Xunit; + +namespace StellaOps.TaskRunner.Tests; + +public sealed class MongoIndexModelTests +{ + [Fact] + public void StateStore_indexes_match_contract() + { + var models = MongoPackRunStateStore.GetIndexModels().ToArray(); + + Assert.Collection(models, + model => Assert.Equal("pack_runs_updatedAt_desc", model.Options.Name), + model => Assert.Equal("pack_runs_tenant_updatedAt_desc", model.Options.Name)); + + Assert.True(models[1].Options.Sparse ?? false); + } + + [Fact] + public void LogStore_indexes_match_contract() + { + var models = MongoPackRunLogStore.GetIndexModels().ToArray(); + + Assert.Collection(models, + model => + { + Assert.Equal("pack_run_logs_run_sequence", model.Options.Name); + Assert.True(model.Options.Unique ?? false); + }, + model => Assert.Equal("pack_run_logs_run_timestamp", model.Options.Name)); + } + + [Fact] + public void ArtifactStore_indexes_match_contract() + { + var models = MongoPackRunArtifactUploader.GetIndexModels().ToArray(); + + Assert.Collection(models, + model => + { + Assert.Equal("pack_artifacts_run_name", model.Options.Name); + Assert.True(model.Options.Unique ?? false); + }, + model => Assert.Equal("pack_artifacts_run", model.Options.Name)); + } + + [Fact] + public void ApprovalStore_indexes_match_contract() + { + var models = MongoPackRunApprovalStore.GetIndexModels().ToArray(); + + Assert.Collection(models, + model => + { + Assert.Equal("pack_run_approvals_run_approval", model.Options.Name); + Assert.True(model.Options.Unique ?? false); + }, + model => Assert.Equal("pack_run_approvals_run_status", model.Options.Name)); + } +} diff --git a/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/OpenApiMetadataFactoryTests.cs b/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/OpenApiMetadataFactoryTests.cs new file mode 100644 index 000000000..66556e962 --- /dev/null +++ b/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/OpenApiMetadataFactoryTests.cs @@ -0,0 +1,27 @@ +using StellaOps.TaskRunner.WebService; + +namespace StellaOps.TaskRunner.Tests; + +public sealed class OpenApiMetadataFactoryTests +{ + [Fact] + public void Create_ProducesExpectedDefaults() + { + var metadata = OpenApiMetadataFactory.Create(); + + Assert.Equal("/openapi", metadata.Url); + Assert.False(string.IsNullOrWhiteSpace(metadata.Build)); + Assert.StartsWith("W/\"", metadata.ETag); + Assert.EndsWith("\"", metadata.ETag); + Assert.Equal(64, metadata.Signature.Length); + Assert.True(metadata.Signature.All(c => char.IsDigit(c) || (c >= 'a' && c <= 'f'))); + } + + [Fact] + public void Create_AllowsOverrideUrl() + { + var metadata = OpenApiMetadataFactory.Create("/docs/openapi.json"); + + Assert.Equal("/docs/openapi.json", metadata.Url); + } +} diff --git a/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.WebService/OpenApiMetadataFactory.cs b/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.WebService/OpenApiMetadataFactory.cs new file mode 100644 index 000000000..6e77e0295 --- /dev/null +++ b/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.WebService/OpenApiMetadataFactory.cs @@ -0,0 +1,38 @@ +using System.Reflection; + +namespace StellaOps.TaskRunner.WebService; + +internal static class OpenApiMetadataFactory +{ + internal static Type ResponseType => typeof(OpenApiMetadata); + + public static OpenApiMetadata Create(string? specUrl = null) + { + var assembly = Assembly.GetExecutingAssembly().GetName(); + var version = assembly.Version?.ToString() ?? "0.0.0"; + var url = string.IsNullOrWhiteSpace(specUrl) ? "/openapi" : specUrl; + var etag = CreateWeakEtag(version); + var signature = ComputeSignature(url, version); + + return new OpenApiMetadata(url, version, etag, signature); + } + + private static string CreateWeakEtag(string input) + { + if (string.IsNullOrWhiteSpace(input)) + { + input = "0.0.0"; + } + + return $"W/\"{input}\""; + } + + private static string ComputeSignature(string url, string build) + { + var data = System.Text.Encoding.UTF8.GetBytes(url + build); + var hash = System.Security.Cryptography.SHA256.HashData(data); + return Convert.ToHexString(hash).ToLowerInvariant(); + } + + internal sealed record OpenApiMetadata(string Url, string Build, string ETag, string Signature); +} diff --git a/src/Zastava/StellaOps.Zastava.Observer/Secrets/ObserverSurfaceSecrets.cs b/src/Zastava/StellaOps.Zastava.Observer/Secrets/ObserverSurfaceSecrets.cs new file mode 100644 index 000000000..8b6af9c71 --- /dev/null +++ b/src/Zastava/StellaOps.Zastava.Observer/Secrets/ObserverSurfaceSecrets.cs @@ -0,0 +1,58 @@ +using Microsoft.Extensions.Options; +using StellaOps.Scanner.Surface.Env; +using StellaOps.Scanner.Surface.Secrets; +using StellaOps.Zastava.Core.Configuration; +using StellaOps.Zastava.Observer.Configuration; + +namespace StellaOps.Zastava.Observer.Secrets; + +internal interface IObserverSurfaceSecrets +{ + ValueTask GetCasAccessAsync(string? name, CancellationToken cancellationToken = default); + ValueTask GetAttestationAsync(string? name, CancellationToken cancellationToken = default); +} + +internal sealed class ObserverSurfaceSecrets : IObserverSurfaceSecrets +{ + private const string Component = "Zastava.Observer"; + + private readonly ISurfaceSecretProvider _provider; + private readonly IOptions _runtime; + private readonly IOptions _observer; + + public ObserverSurfaceSecrets( + ISurfaceSecretProvider provider, + IOptions runtime, + IOptions observer) + { + _provider = provider ?? throw new ArgumentNullException(nameof(provider)); + _runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); + _observer = observer ?? throw new ArgumentNullException(nameof(observer)); + } + + public async ValueTask GetCasAccessAsync(string? name, CancellationToken cancellationToken = default) + { + var options = _observer.Value.Secrets; + var request = new SurfaceSecretRequest( + Tenant: _runtime.Value.Tenant, + Component: Component, + SecretType: "cas-access", + Name: string.IsNullOrWhiteSpace(name) ? options.CasAccessName : name); + + var handle = await _provider.GetAsync(request, cancellationToken).ConfigureAwait(false); + return SurfaceSecretParser.ParseCasAccessSecret(handle); + } + + public async ValueTask GetAttestationAsync(string? name, CancellationToken cancellationToken = default) + { + var options = _observer.Value.Secrets; + var request = new SurfaceSecretRequest( + Tenant: _runtime.Value.Tenant, + Component: Component, + SecretType: "attestation", + Name: string.IsNullOrWhiteSpace(name) ? options.AttestationName : name); + + var handle = await _provider.GetAsync(request, cancellationToken).ConfigureAwait(false); + return SurfaceSecretParser.ParseAttestationSecret(handle); + } +} diff --git a/src/Zastava/StellaOps.Zastava.Observer/Surface/RuntimeSurfaceFsClient.cs b/src/Zastava/StellaOps.Zastava.Observer/Surface/RuntimeSurfaceFsClient.cs new file mode 100644 index 000000000..b61d38821 --- /dev/null +++ b/src/Zastava/StellaOps.Zastava.Observer/Surface/RuntimeSurfaceFsClient.cs @@ -0,0 +1,32 @@ +using StellaOps.Scanner.Surface.Env; +using StellaOps.Scanner.Surface.FS; + +namespace StellaOps.Zastava.Observer.Surface; + +internal interface IRuntimeSurfaceFsClient +{ + Task TryGetManifestAsync(string manifestDigest, CancellationToken cancellationToken = default); +} + +internal sealed class RuntimeSurfaceFsClient : IRuntimeSurfaceFsClient +{ + private readonly ISurfaceManifestReader _manifestReader; + private readonly SurfaceEnvironmentSettings _environment; + + public RuntimeSurfaceFsClient(ISurfaceManifestReader manifestReader, SurfaceEnvironmentSettings environment) + { + _manifestReader = manifestReader ?? throw new ArgumentNullException(nameof(manifestReader)); + _environment = environment ?? throw new ArgumentNullException(nameof(environment)); + } + + public Task TryGetManifestAsync(string manifestDigest, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(manifestDigest)) + { + return Task.FromResult(null); + } + + // manifest digests follow sha256:; manifest reader handles validation and tenant discovery + return _manifestReader.TryGetByDigestAsync(manifestDigest.Trim(), cancellationToken); + } +} diff --git a/src/Zastava/StellaOps.Zastava.Webhook/Secrets/WebhookSurfaceSecrets.cs b/src/Zastava/StellaOps.Zastava.Webhook/Secrets/WebhookSurfaceSecrets.cs new file mode 100644 index 000000000..5cda74629 --- /dev/null +++ b/src/Zastava/StellaOps.Zastava.Webhook/Secrets/WebhookSurfaceSecrets.cs @@ -0,0 +1,43 @@ +using Microsoft.Extensions.Options; +using StellaOps.Scanner.Surface.Secrets; +using StellaOps.Zastava.Core.Configuration; +using StellaOps.Zastava.Webhook.Configuration; + +namespace StellaOps.Zastava.Webhook.Secrets; + +internal interface IWebhookSurfaceSecrets +{ + ValueTask GetAttestationAsync(string? name, CancellationToken cancellationToken = default); +} + +internal sealed class WebhookSurfaceSecrets : IWebhookSurfaceSecrets +{ + private const string Component = "Zastava.Webhook"; + + private readonly ISurfaceSecretProvider _provider; + private readonly IOptions _runtime; + private readonly IOptions _webhook; + + public WebhookSurfaceSecrets( + ISurfaceSecretProvider provider, + IOptions runtime, + IOptions webhook) + { + _provider = provider ?? throw new ArgumentNullException(nameof(provider)); + _runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); + _webhook = webhook ?? throw new ArgumentNullException(nameof(webhook)); + } + + public async ValueTask GetAttestationAsync(string? name, CancellationToken cancellationToken = default) + { + var options = _webhook.Value.Secrets; + var request = new SurfaceSecretRequest( + Tenant: _runtime.Value.Tenant, + Component: Component, + SecretType: "attestation", + Name: string.IsNullOrWhiteSpace(name) ? options.AttestationName : name); + + var handle = await _provider.GetAsync(request, cancellationToken).ConfigureAwait(false); + return SurfaceSecretParser.ParseAttestationSecret(handle); + } +} diff --git a/src/Zastava/StellaOps.Zastava.Webhook/Surface/WebhookSurfaceFsClient.cs b/src/Zastava/StellaOps.Zastava.Webhook/Surface/WebhookSurfaceFsClient.cs new file mode 100644 index 000000000..5d0523227 --- /dev/null +++ b/src/Zastava/StellaOps.Zastava.Webhook/Surface/WebhookSurfaceFsClient.cs @@ -0,0 +1,65 @@ +using Microsoft.Extensions.Options; +using StellaOps.Scanner.Surface.FS; +using StellaOps.Zastava.Core.Configuration; + +namespace StellaOps.Zastava.Webhook.Surface; + +internal interface IWebhookSurfaceFsClient +{ + Task<(bool Found, string? ManifestUri)> TryGetManifestAsync(string manifestDigest, CancellationToken cancellationToken = default); +} + +internal sealed class WebhookSurfaceFsClient : IWebhookSurfaceFsClient +{ + private readonly ISurfaceManifestReader _manifestReader; + private readonly SurfaceManifestPathBuilder _pathBuilder; + private readonly IOptions _runtimeOptions; + + public WebhookSurfaceFsClient( + ISurfaceManifestReader manifestReader, + IOptions cacheOptions, + IOptions storeOptions, + IOptions runtimeOptions) + { + _manifestReader = manifestReader ?? throw new ArgumentNullException(nameof(manifestReader)); + _runtimeOptions = runtimeOptions ?? throw new ArgumentNullException(nameof(runtimeOptions)); + + if (cacheOptions is null) + { + throw new ArgumentNullException(nameof(cacheOptions)); + } + + if (storeOptions is null) + { + throw new ArgumentNullException(nameof(storeOptions)); + } + + _pathBuilder = new SurfaceManifestPathBuilder(cacheOptions.Value, storeOptions.Value); + } + + public async Task<(bool Found, string? ManifestUri)> TryGetManifestAsync(string manifestDigest, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(manifestDigest)) + { + return (false, null); + } + + cancellationToken.ThrowIfCancellationRequested(); + + // First check whether the manifest exists in the local surface store. + var manifest = await _manifestReader.TryGetByDigestAsync(manifestDigest.Trim(), cancellationToken).ConfigureAwait(false); + if (manifest is null) + { + return (false, null); + } + + var tenant = !string.IsNullOrWhiteSpace(manifest.Tenant) + ? manifest.Tenant + : _runtimeOptions.Value.Tenant; + + var digestHex = SurfaceManifestPathBuilder.EnsureSha256Digest(manifestDigest); // strips sha256: + var uri = _pathBuilder.BuildManifestUri(tenant, digestHex); + + return (true, uri); + } +} diff --git a/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/DependencyInjection/SurfaceEnvironmentRegistrationTests.cs b/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/DependencyInjection/SurfaceEnvironmentRegistrationTests.cs new file mode 100644 index 000000000..9dd1a4e48 --- /dev/null +++ b/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/DependencyInjection/SurfaceEnvironmentRegistrationTests.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using StellaOps.Scanner.Surface.Env; +using StellaOps.Zastava.Observer.Configuration; +using Xunit; + +namespace StellaOps.Zastava.Observer.Tests.DependencyInjection; + +public sealed class SurfaceEnvironmentRegistrationTests +{ + [Fact] + public void AddZastavaObserver_RegistersSurfaceEnvironmentWithZastavaPrefixes() + { + var env = new Dictionary + { + ["ZASTAVA_SURFACE_FS_ENDPOINT"] = "https://surface.example", + ["ZASTAVA_SURFACE_FS_BUCKET"] = "zastava-cache", + ["ZASTAVA_SURFACE_FEATURES"] = "prefetch,drift", + ["ZASTAVA_SURFACE_SECRETS_PROVIDER"] = "kubernetes", + ["ZASTAVA_SURFACE_SECRETS_NAMESPACE"] = "security", + ["ZASTAVA_SURFACE_TENANT"] = "tenant-a" + }; + + var originals = CaptureEnvironment(env.Keys); + try + { + foreach (var pair in env) + { + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [$"{ZastavaObserverOptions.SectionName}:runtimes:0:engine"] = "Containerd" + }) + .Build(); + + var services = new ServiceCollection(); + services.AddLogging(); + services.AddZastavaObserver(configuration); + + using var provider = services.BuildServiceProvider(); + var surface = provider.GetRequiredService(); + var settings = provider.GetRequiredService(); + + Assert.Equal(new Uri("https://surface.example"), settings.SurfaceFsEndpoint); + Assert.Equal("zastava-cache", settings.SurfaceFsBucket); + Assert.Contains("prefetch", settings.FeatureFlags, StringComparer.OrdinalIgnoreCase); + Assert.Contains("drift", settings.FeatureFlags, StringComparer.OrdinalIgnoreCase); + Assert.Equal("kubernetes", settings.Secrets.Provider); + Assert.Equal("security", settings.Secrets.Namespace); + Assert.Equal("tenant-a", settings.Tenant); + + // Ensure singleton mapping is shared + Assert.Same(surface.Settings, settings); + } + finally + { + RestoreEnvironment(originals); + } + } + + private static IReadOnlyDictionary CaptureEnvironment(IEnumerable names) + { + var snapshot = new Dictionary(); + foreach (var name in names) + { + snapshot[name] = Environment.GetEnvironmentVariable(name); + } + + return snapshot; + } + + private static void RestoreEnvironment(IReadOnlyDictionary snapshot) + { + foreach (var pair in snapshot) + { + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + } +} diff --git a/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/Secrets/ObserverSurfaceSecretsTests.cs b/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/Secrets/ObserverSurfaceSecretsTests.cs new file mode 100644 index 000000000..5731e5d16 --- /dev/null +++ b/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/Secrets/ObserverSurfaceSecretsTests.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using StellaOps.Scanner.Surface.Env; +using StellaOps.Zastava.Observer.Configuration; +using StellaOps.Zastava.Observer.Secrets; +using Xunit; + +namespace StellaOps.Zastava.Observer.Tests.Secrets; + +public sealed class ObserverSurfaceSecretsTests +{ + [Fact] + public async Task GetCasAccessAsync_ResolvesInlineSecret() + { + var env = new Dictionary + { + ["ZASTAVA_SURFACE_FS_ENDPOINT"] = "https://surface.example", + ["ZASTAVA_SURFACE_SECRETS_PROVIDER"] = "inline", + ["ZASTAVA_SURFACE_SECRETS_ALLOW_INLINE"] = "true", + ["SURFACE_SECRET_DEFAULT_ZASTAVA.OBSERVER_CAS-ACCESS_PRIMARY"] = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("{\"driver\":\"s3\",\"accessKeyId\":\"ak\",\"secretAccessKey\":\"sk\"}")) + }; + + var services = BuildServices(env); + + var secrets = services.GetRequiredService(); + var result = await secrets.GetCasAccessAsync(name: null); + + Assert.Equal("s3", result.Driver); + Assert.Equal("ak", result.AccessKeyId); + Assert.Equal("sk", result.SecretAccessKey); + } + + [Fact] + public async Task GetAttestationAsync_ResolvesInlineSecret() + { + var payload = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("{\"keyPem\":\"KEY\",\"rekorToken\":\"token123\"}")); + var env = new Dictionary + { + ["ZASTAVA_SURFACE_FS_ENDPOINT"] = "https://surface.example", + ["ZASTAVA_SURFACE_SECRETS_PROVIDER"] = "inline", + ["ZASTAVA_SURFACE_SECRETS_ALLOW_INLINE"] = "true", + ["SURFACE_SECRET_DEFAULT_ZASTAVA.OBSERVER_ATTESTATION_SIGNING"] = payload + }; + + var services = BuildServices(env); + + var secrets = services.GetRequiredService(); + var result = await secrets.GetAttestationAsync(name: null); + + Assert.Equal("KEY", result.KeyPem); + Assert.Equal("token123", result.RekorApiToken); + } + + private static ServiceProvider BuildServices(Dictionary env) + { + var originals = new Dictionary(); + foreach (var pair in env) + { + originals[pair.Key] = Environment.GetEnvironmentVariable(pair.Key); + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [$"{ZastavaObserverOptions.SectionName}:runtimes:0:engine"] = "Containerd" + }) + .Build(); + + var services = new ServiceCollection(); + services.AddLogging(); + services.AddZastavaObserver(configuration); + + var provider = services.BuildServiceProvider(); + + foreach (var pair in originals) + { + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + + return provider; + } +} diff --git a/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/Surface/RuntimeSurfaceFsClientTests.cs b/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/Surface/RuntimeSurfaceFsClientTests.cs new file mode 100644 index 000000000..547a58217 --- /dev/null +++ b/src/Zastava/__Tests/StellaOps.Zastava.Observer.Tests/Surface/RuntimeSurfaceFsClientTests.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using StellaOps.Scanner.Surface.FS; +using StellaOps.Zastava.Observer.Configuration; +using StellaOps.Zastava.Observer.Surface; +using Xunit; + +namespace StellaOps.Zastava.Observer.Tests.Surface; + +public sealed class RuntimeSurfaceFsClientTests +{ + [Fact] + public async Task TryGetManifestAsync_ReturnsPublishedManifest() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [$"{ZastavaObserverOptions.SectionName}:runtimes:0:engine"] = "Containerd", + [$"{ZastavaObserverOptions.SectionName}:backend:baseAddress"] = "https://scanner.internal" + }) + .Build(); + + var services = new ServiceCollection(); + services.AddLogging(); + services.AddZastavaObserver(configuration); + + using var provider = services.BuildServiceProvider(); + var manifestWriter = provider.GetRequiredService(); + var client = provider.GetRequiredService(); + + var document = new SurfaceManifestDocument + { + Tenant = "default", + ImageDigest = "sha256:deadbeef", + GeneratedAt = DateTimeOffset.UtcNow, + Artifacts = new[] + { + new SurfaceManifestArtifact + { + Kind = "entry-trace", + Uri = "cas://surface-cache/manifest/entry-trace", + Digest = "sha256:abc123", + MediaType = "application/json", + Format = "ndsjon" + } + } + }; + + var published = await manifestWriter.PublishAsync(document, default); + var fetched = await client.TryGetManifestAsync(published.ManifestDigest, default); + + Assert.NotNull(fetched); + Assert.Equal(document.Tenant, fetched!.Tenant); + Assert.Equal(document.ImageDigest, fetched.ImageDigest); + } +} diff --git a/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/DependencyInjection/SurfaceEnvironmentRegistrationTests.cs b/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/DependencyInjection/SurfaceEnvironmentRegistrationTests.cs new file mode 100644 index 000000000..9c9edfe91 --- /dev/null +++ b/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/DependencyInjection/SurfaceEnvironmentRegistrationTests.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using StellaOps.Scanner.Surface.Env; +using StellaOps.Zastava.Webhook.Configuration; +using Xunit; + +namespace StellaOps.Zastava.Webhook.Tests.DependencyInjection; + +public sealed class SurfaceEnvironmentRegistrationTests +{ + [Fact] + public void AddZastavaWebhook_RegistersSurfaceEnvironmentWithZastavaPrefixes() + { + var env = new Dictionary + { + ["ZASTAVA_WEBHOOK_SURFACE_FS_ENDPOINT"] = "https://surface.example", + ["ZASTAVA_WEBHOOK_SURFACE_FS_BUCKET"] = "zastava-cache", + ["ZASTAVA_WEBHOOK_SURFACE_FEATURES"] = "admission,drift", + ["ZASTAVA_WEBHOOK_SURFACE_SECRETS_PROVIDER"] = "kubernetes", + ["ZASTAVA_WEBHOOK_SURFACE_SECRETS_NAMESPACE"] = "security", + ["ZASTAVA_WEBHOOK_SURFACE_TENANT"] = "tenant-w" + }; + + var originals = CaptureEnvironment(env.Keys); + try + { + foreach (var pair in env) + { + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [$"{ZastavaWebhookOptions.SectionName}:tls:mode"] = "Secret" + }) + .Build(); + + var services = new ServiceCollection(); + services.AddLogging(); + services.AddZastavaWebhook(configuration); + + using var provider = services.BuildServiceProvider(); + var surface = provider.GetRequiredService(); + var settings = provider.GetRequiredService(); + + Assert.Equal(new Uri("https://surface.example"), settings.SurfaceFsEndpoint); + Assert.Equal("zastava-cache", settings.SurfaceFsBucket); + Assert.Contains("admission", settings.FeatureFlags, StringComparer.OrdinalIgnoreCase); + Assert.Contains("drift", settings.FeatureFlags, StringComparer.OrdinalIgnoreCase); + Assert.Equal("kubernetes", settings.Secrets.Provider); + Assert.Equal("security", settings.Secrets.Namespace); + Assert.Equal("tenant-w", settings.Tenant); + + Assert.Same(surface.Settings, settings); + } + finally + { + RestoreEnvironment(originals); + } + } + + private static IReadOnlyDictionary CaptureEnvironment(IEnumerable names) + { + var snapshot = new Dictionary(); + foreach (var name in names) + { + snapshot[name] = Environment.GetEnvironmentVariable(name); + } + + return snapshot; + } + + private static void RestoreEnvironment(IReadOnlyDictionary snapshot) + { + foreach (var pair in snapshot) + { + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + } +} diff --git a/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/DependencyInjection/SurfaceSecretsRegistrationTests.cs b/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/DependencyInjection/SurfaceSecretsRegistrationTests.cs new file mode 100644 index 000000000..5362bb122 --- /dev/null +++ b/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/DependencyInjection/SurfaceSecretsRegistrationTests.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using StellaOps.Scanner.Surface.Env; +using StellaOps.Zastava.Webhook.Configuration; +using StellaOps.Zastava.Webhook.Secrets; +using Xunit; + +namespace StellaOps.Zastava.Webhook.Tests.DependencyInjection; + +public sealed class SurfaceSecretsRegistrationTests +{ + [Fact] + public async Task AddZastavaWebhook_ResolvesInlineAttestationSecret() + { + var payload = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("{\"keyPem\":\"KEY\",\"rekorToken\":\"rekor\"}")); + var env = new Dictionary + { + ["ZASTAVA_WEBHOOK_SURFACE_FS_ENDPOINT"] = "https://surface.example", + ["ZASTAVA_WEBHOOK_SURFACE_SECRETS_PROVIDER"] = "inline", + ["ZASTAVA_WEBHOOK_SURFACE_SECRETS_ALLOW_INLINE"] = "true", + ["SURFACE_SECRET_DEFAULT_ZASTAVA.WEBHOOK_ATTESTATION_VERIFICATION"] = payload + }; + + var services = BuildServices(env); + + var secrets = services.GetRequiredService(); + var result = await secrets.GetAttestationAsync(name: null); + + Assert.Equal("KEY", result.KeyPem); + Assert.Equal("rekor", result.RekorApiToken); + } + + private static ServiceProvider BuildServices(Dictionary env) + { + var originals = new Dictionary(); + foreach (var pair in env) + { + originals[pair.Key] = Environment.GetEnvironmentVariable(pair.Key); + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [$"{ZastavaWebhookOptions.SectionName}:tls:mode"] = "Secret" + }) + .Build(); + + var services = new ServiceCollection(); + services.AddLogging(); + services.AddZastavaWebhook(configuration); + + var provider = services.BuildServiceProvider(); + + foreach (var pair in originals) + { + Environment.SetEnvironmentVariable(pair.Key, pair.Value); + } + + return provider; + } +} diff --git a/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/Surface/WebhookSurfaceFsClientTests.cs b/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/Surface/WebhookSurfaceFsClientTests.cs new file mode 100644 index 000000000..491055f2c --- /dev/null +++ b/src/Zastava/__Tests/StellaOps.Zastava.Webhook.Tests/Surface/WebhookSurfaceFsClientTests.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using StellaOps.Scanner.Surface.FS; +using StellaOps.Zastava.Webhook.Configuration; +using StellaOps.Zastava.Webhook.Surface; +using Xunit; + +namespace StellaOps.Zastava.Webhook.Tests.Surface; + +public sealed class WebhookSurfaceFsClientTests +{ + [Fact] + public async Task TryGetManifestAsync_ReturnsPointerWhenPresent() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [$"{ZastavaWebhookOptions.SectionName}:tls:mode"] = "Secret" + }) + .Build(); + + var services = new ServiceCollection(); + services.AddLogging(); + services.AddZastavaWebhook(configuration); + + using var provider = services.BuildServiceProvider(); + var writer = provider.GetRequiredService(); + var client = provider.GetRequiredService(); + + var doc = new SurfaceManifestDocument + { + Tenant = "default", + ImageDigest = "sha256:deadbeef", + GeneratedAt = DateTimeOffset.UtcNow, + Artifacts = new[] + { + new SurfaceManifestArtifact + { + Kind = "entry-trace", + Uri = "cas://surface-cache/manifest/entry-trace", + Digest = "sha256:abc123", + MediaType = "application/json", + Format = "ndjson" + } + } + }; + + var published = await writer.PublishAsync(doc, default); + var (found, pointer) = await client.TryGetManifestAsync(published.ManifestDigest, default); + + Assert.True(found); + Assert.NotNull(pointer); + Assert.Contains("surface-cache", pointer); // matches default bucket + } +} diff --git a/tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests/EnsureLinkNotMergeCollectionsMigrationTests.cs b/tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests/EnsureLinkNotMergeCollectionsMigrationTests.cs new file mode 100644 index 000000000..6e8dd06af --- /dev/null +++ b/tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests/EnsureLinkNotMergeCollectionsMigrationTests.cs @@ -0,0 +1,70 @@ +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Mongo2Go; +using MongoDB.Bson; +using MongoDB.Driver; +using StellaOps.Concelier.Storage.Mongo.Migrations; + +namespace StellaOps.Concelier.Storage.Mongo.Tests; + +public sealed class EnsureLinkNotMergeCollectionsMigrationTests : IAsyncLifetime +{ + private MongoDbRunner _runner = null!; + private IMongoDatabase _database = null!; + + public Task InitializeAsync() + { + _runner = MongoDbRunner.Start(singleNodeReplSet: true); + var client = new MongoClient(_runner.ConnectionString); + _database = client.GetDatabase("lnm-migration-tests"); + return Task.CompletedTask; + } + + public Task DisposeAsync() + { + _runner.Dispose(); + return Task.CompletedTask; + } + + [Fact] + public async Task CreatesCollectionsAndIndexesIdempotently() + { + var migration = new EnsureLinkNotMergeCollectionsMigration(); + + await migration.ApplyAsync(_database, CancellationToken.None); + await migration.ApplyAsync(_database, CancellationToken.None); // idempotent second run + + var collections = await _database.ListCollectionNames().ToListAsync(); + collections.Should().Contain(new[] + { + MongoStorageDefaults.Collections.AdvisoryObservations, + MongoStorageDefaults.Collections.AdvisoryLinksets + }); + + var linksetIndexes = await _database + .GetCollection(MongoStorageDefaults.Collections.AdvisoryLinksets) + .Indexes.List() + .ToListAsync(); + + linksetIndexes.Should().ContainSingle(i => i["name"] == "linkset_tenant_advisory_source" && i["unique"].AsBoolean); + + var obsIndexes = await _database + .GetCollection(MongoStorageDefaults.Collections.AdvisoryObservations) + .Indexes.List() + .ToListAsync(); + + obsIndexes.Should().Contain(i => i["name"] == "obs_prov_sourceArtifactSha_unique" && i["unique"].AsBoolean); + + var linksetValidator = await GetValidatorAsync(MongoStorageDefaults.Collections.AdvisoryLinksets); + linksetValidator.Should().NotBeNull(); + } + + private async Task GetValidatorAsync(string collection) + { + var filter = new BsonDocument("name", collection); + var cursor = await _database.ListCollectionsAsync(new ListCollectionsOptions { Filter = filter }); + var doc = await cursor.FirstOrDefaultAsync(); + return doc?"options"?"validator"?.AsBsonDocument; + } +} diff --git a/tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests/StellaOps.Concelier.Storage.Mongo.Tests.csproj b/tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests/StellaOps.Concelier.Storage.Mongo.Tests.csproj new file mode 100644 index 000000000..af9a2835f --- /dev/null +++ b/tests/Concelier/StellaOps.Concelier.Storage.Mongo.Tests/StellaOps.Concelier.Storage.Mongo.Tests.csproj @@ -0,0 +1,21 @@ + + + net10.0 + enable + enable + false + + + + + + + + + + + + + + + diff --git a/tools/nuget-prime/mirror-packages.txt b/tools/nuget-prime/mirror-packages.txt new file mode 100644 index 000000000..3a98690f7 --- /dev/null +++ b/tools/nuget-prime/mirror-packages.txt @@ -0,0 +1,31 @@ +AWSSDK.S3|3.7.305.6 +CycloneDX.Core|10.0.1 +Google.Protobuf|3.27.2 +Grpc.Net.Client|2.65.0 +Grpc.Tools|2.65.0 +Microsoft.Data.Sqlite|9.0.0-rc.1.24451.1 +Microsoft.Extensions.Configuration.Abstractions|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Configuration.Abstractions|9.0.0 +Microsoft.Extensions.Configuration.Binder|10.0.0-rc.2.25502.107 +Microsoft.Extensions.DependencyInjection.Abstractions|10.0.0-rc.2.25502.107 +Microsoft.Extensions.DependencyInjection.Abstractions|9.0.0 +Microsoft.Extensions.Diagnostics.Abstractions|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Diagnostics.HealthChecks|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Hosting.Abstractions|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Http.Polly|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Http|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Logging.Abstractions|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Logging.Abstractions|9.0.0 +Microsoft.Extensions.Options.ConfigurationExtensions|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Options|10.0.0-rc.2.25502.107 +Microsoft.Extensions.Options|9.0.0 +MongoDB.Driver|3.5.0 +NATS.Client.Core|2.0.0 +NATS.Client.JetStream|2.0.0 +RoaringBitmap|0.0.9 +Serilog.AspNetCore|8.0.1 +Serilog.Extensions.Hosting|8.0.0 +Serilog.Sinks.Console|5.0.1 +StackExchange.Redis|2.7.33 +System.Text.Json|10.0.0-preview.7.25380.108 diff --git a/tools/nuget-prime/nuget-prime.csproj b/tools/nuget-prime/nuget-prime.csproj new file mode 100644 index 000000000..19ce0c0c4 --- /dev/null +++ b/tools/nuget-prime/nuget-prime.csproj @@ -0,0 +1,43 @@ + + + net10.0 + ../../local-nuget + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +