Files
git.stella-ops.org/docs/implplan/UNBLOCK_IMPLEMENTATION_PLAN.md
StellaOps Bot 8768c27f30
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals DSSE Sign & Evidence Locker / sign-signals-artifacts (push) Has been cancelled
Signals DSSE Sign & Evidence Locker / verify-signatures (push) Has been cancelled
Add signal contracts for reachability, exploitability, trust, and unknown symbols
- Introduced `ReachabilityState`, `RuntimeHit`, `ExploitabilitySignal`, `ReachabilitySignal`, `SignalEnvelope`, `SignalType`, `TrustSignal`, and `UnknownSymbolSignal` records to define various signal types and their properties.
- Implemented JSON serialization attributes for proper data interchange.
- Created project files for the new signal contracts library and corresponding test projects.
- Added deterministic test fixtures for micro-interaction testing.
- Included cryptographic keys for secure operations with cosign.
2025-12-05 00:27:00 +02:00

12 KiB

Blocker Unblock Implementation Plan

Created: 2025-12-04 Purpose: Step-by-step implementation plan to unblock remaining ~14 tasks Estimated Effort: 16-22 developer-days

Executive Summary

After creating 11 specification contracts that unblocked ~61 tasks, we have 14 remaining blocked tasks that require actual implementation work (not just specs). This plan outlines the implementation roadmap.


Remaining Blockers Analysis

Blocker Tasks Blocked Type Complexity
WEB-POLICY-20-004 (Rate Limiting) 6 Code Implementation SIMPLE
Shared Signals Library 5+ New Library MODERATE
Postgres Repositories 5 Code Implementation MODERATE
Test Infrastructure N/A Infrastructure MODERATE
PGMI0101 Staffing 3 Human Decision N/A

Implementation Phases

Phase 1: Policy Engine Rate Limiting (WEB-POLICY-20-004)

Duration: 1-2 days Unblocks: 6 tasks (WEB-POLICY-20-004 chain) Dependencies: None

1.1 Create Rate Limit Options

File: src/Policy/StellaOps.Policy.Engine/Options/PolicyEngineRateLimitOptions.cs

namespace StellaOps.Policy.Engine.Options;

public sealed class PolicyEngineRateLimitOptions
{
    public const string SectionName = "RateLimiting";

    /// <summary>Default permits per window for simulation endpoints</summary>
    public int SimulationPermitLimit { get; set; } = 100;

    /// <summary>Window duration in seconds</summary>
    public int WindowSeconds { get; set; } = 60;

    /// <summary>Queue limit for pending requests</summary>
    public int QueueLimit { get; set; } = 10;

    /// <summary>Enable tenant-aware partitioning</summary>
    public bool TenantPartitioning { get; set; } = true;
}

1.2 Register Rate Limiter in Program.cs

Add to src/Policy/StellaOps.Policy.Engine/Program.cs:

// Rate limiting configuration
var rateLimitOptions = builder.Configuration
    .GetSection(PolicyEngineRateLimitOptions.SectionName)
    .Get<PolicyEngineRateLimitOptions>() ?? new();

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;

    options.AddTokenBucketLimiter("policy-simulation", limiterOptions =>
    {
        limiterOptions.TokenLimit = rateLimitOptions.SimulationPermitLimit;
        limiterOptions.ReplenishmentPeriod = TimeSpan.FromSeconds(rateLimitOptions.WindowSeconds);
        limiterOptions.TokensPerPeriod = rateLimitOptions.SimulationPermitLimit;
        limiterOptions.QueueLimit = rateLimitOptions.QueueLimit;
        limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
    });

    options.OnRejected = async (context, cancellationToken) =>
    {
        PolicyEngineTelemetry.RateLimitExceededCounter.Add(1);
        context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
        await context.HttpContext.Response.WriteAsJsonAsync(new
        {
            error = "ERR_POL_007",
            message = "Rate limit exceeded. Please retry after the reset window.",
            retryAfterSeconds = rateLimitOptions.WindowSeconds
        }, cancellationToken);
    };
});

1.3 Apply to Simulation Endpoints

Modify src/Policy/StellaOps.Policy.Engine/Endpoints/RiskSimulationEndpoints.cs:

group.MapPost("/simulate", SimulateRisk)
    .RequireRateLimiting("policy-simulation")  // ADD THIS
    .WithName("SimulateRisk");

1.4 Add Telemetry Counter

Add to src/Policy/StellaOps.Policy.Engine/Telemetry/PolicyEngineTelemetry.cs:

public static readonly Counter<long> RateLimitExceededCounter =
    Meter.CreateCounter<long>(
        "policy_rate_limit_exceeded_total",
        unit: "requests",
        description: "Total requests rejected due to rate limiting");

1.5 Configuration Sample

Add to etc/policy-engine.yaml.sample:

RateLimiting:
  SimulationPermitLimit: 100
  WindowSeconds: 60
  QueueLimit: 10
  TenantPartitioning: true

Phase 2: Shared Signals Contracts Library

Duration: 3-4 days Unblocks: 5+ modules (Concelier, Scanner, Policy, Signals, Authority) Dependencies: None

2.1 Create Project Structure

src/__Libraries/StellaOps.Signals.Contracts/
├── StellaOps.Signals.Contracts.csproj
├── AGENTS.md
├── Models/
│   ├── SignalEnvelope.cs
│   ├── SignalType.cs
│   ├── ReachabilitySignal.cs
│   ├── EntropySignal.cs
│   ├── ExploitabilitySignal.cs
│   ├── TrustSignal.cs
│   └── UnknownSymbolSignal.cs
├── Abstractions/
│   ├── ISignalEmitter.cs
│   ├── ISignalConsumer.cs
│   └── ISignalContext.cs
└── Extensions/
    └── ServiceCollectionExtensions.cs

2.2 Core Models

SignalEnvelope.cs:

namespace StellaOps.Signals.Contracts;

public sealed record SignalEnvelope(
    string SignalKey,
    SignalType SignalType,
    object Value,
    DateTimeOffset ComputedAt,
    string SourceService,
    string? TenantId = null,
    string? CorrelationId = null,
    string? ProvenanceDigest = null);

SignalType.cs:

namespace StellaOps.Signals.Contracts;

public enum SignalType
{
    Reachability,
    Entropy,
    Exploitability,
    Trust,
    UnknownSymbol,
    Custom
}

2.3 Signal Models

Each signal type gets a dedicated record:

  • ReachabilitySignal - package reachability from callgraph
  • EntropySignal - code complexity/risk metrics
  • ExploitabilitySignal - KEV status, exploit availability
  • TrustSignal - reputation, chain of custody scores
  • UnknownSymbolSignal - unresolved dependencies

2.4 Abstractions

public interface ISignalEmitter
{
    ValueTask EmitAsync(SignalEnvelope signal, CancellationToken ct = default);
    ValueTask EmitBatchAsync(IEnumerable<SignalEnvelope> signals, CancellationToken ct = default);
}

public interface ISignalConsumer
{
    IAsyncEnumerable<SignalEnvelope> ConsumeAsync(
        SignalType? filterType = null,
        CancellationToken ct = default);
}

Phase 3: Postgres Repositories

Duration: 4-5 days Unblocks: Persistence for new features Dependencies: SQL migrations

3.1 Repository Interfaces

Create in src/Policy/__Libraries/StellaOps.Policy.Storage.Postgres/Repositories/:

Interface Methods
ISnapshotRepository Create, GetById, List, Delete
IViolationEventRepository Append, GetById, List (immutable)
IWorkerResultRepository Create, GetById, List, Update
IConflictRepository Create, GetById, List, Resolve
ILedgerExportRepository Create, GetById, List, GetByDigest

3.2 SQL Migrations

Create migrations for tables:

-- policy.snapshots
CREATE TABLE policy.snapshots (
    id UUID PRIMARY KEY,
    tenant_id TEXT NOT NULL,
    policy_id UUID NOT NULL,
    version INTEGER NOT NULL,
    content_digest TEXT NOT NULL,
    metadata JSONB,
    created_by TEXT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- policy.violation_events (append-only)
CREATE TABLE policy.violation_events (
    id UUID PRIMARY KEY,
    tenant_id TEXT NOT NULL,
    policy_id UUID NOT NULL,
    rule_id TEXT NOT NULL,
    severity TEXT NOT NULL,
    subject_purl TEXT,
    details JSONB,
    occurred_at TIMESTAMPTZ NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Similar for conflicts, worker_results, ledger_exports

3.3 Implementation Pattern

Follow RiskProfileRepository.cs pattern:

public sealed class SnapshotRepository : RepositoryBase<PolicyDataSource>, ISnapshotRepository
{
    public SnapshotRepository(PolicyDataSource dataSource, ILogger<SnapshotRepository> logger)
        : base(dataSource, logger) { }

    public async Task<SnapshotEntity> CreateAsync(SnapshotEntity entity, CancellationToken ct)
    {
        const string sql = """
            INSERT INTO policy.snapshots
            (id, tenant_id, policy_id, version, content_digest, metadata, created_by)
            VALUES (@Id, @TenantId, @PolicyId, @Version, @ContentDigest, @Metadata::jsonb, @CreatedBy)
            RETURNING *
            """;

        return await ExecuteScalarAsync<SnapshotEntity>(sql, entity, ct);
    }

    // ... other CRUD methods
}

Phase 4: Test Infrastructure

Duration: 2-3 days Unblocks: Validation before merge Dependencies: Phase 3

4.1 Postgres Test Fixture

public sealed class PostgresFixture : IAsyncLifetime
{
    private TestcontainersContainer? _container;
    public string ConnectionString { get; private set; } = string.Empty;

    public async Task InitializeAsync()
    {
        _container = new TestcontainersBuilder<TestcontainersContainer>()
            .WithImage("postgres:16-alpine")
            .WithEnvironment("POSTGRES_PASSWORD", "test")
            .WithPortBinding(5432, true)
            .Build();

        await _container.StartAsync();
        ConnectionString = $"Host=localhost;Port={_container.GetMappedPublicPort(5432)};...";

        // Run migrations
        await MigrationRunner.RunAsync(ConnectionString);
    }

    public async Task DisposeAsync() => await _container?.DisposeAsync();
}

4.2 Test Classes

  • RateLimitingTests.cs - quota exhaustion, recovery, tenant partitioning
  • SnapshotRepositoryTests.cs - CRUD operations
  • ViolationEventRepositoryTests.cs - append-only semantics
  • ConflictRepositoryTests.cs - resolution workflow
  • SignalEnvelopeTests.cs - serialization, validation

Phase 5: New Endpoints

Duration: 2-3 days Unblocks: API surface completion Dependencies: Phase 3

5.1 Endpoint Groups

Path Operations Auth
/api/policy/snapshots GET, POST, DELETE policy:read, policy:author
/api/policy/violations GET policy:read
/api/policy/conflicts GET, POST (resolve) policy:read, policy:review
/api/policy/exports GET, POST policy:read, policy:archive

Execution Order

Day 1-2: Phase 1 (Rate Limiting)
         └── WEB-POLICY-20-004 ✓ UNBLOCKED

Day 3-5: Phase 2 (Signals Library)
         └── Concelier, Scanner, Policy, Signals, Authority ✓ ENABLED

Day 6-9: Phase 3 (Repositories)
         └── Persistence layer ✓ COMPLETE

Day 10-12: Phase 4 (Tests)
           └── Validation ✓ READY

Day 13-15: Phase 5 (Endpoints)
           └── API surface ✓ COMPLETE

Files to Create/Modify Summary

New Files (22 files)

src/Policy/StellaOps.Policy.Engine/Options/
└── PolicyEngineRateLimitOptions.cs

src/__Libraries/StellaOps.Signals.Contracts/
├── StellaOps.Signals.Contracts.csproj
├── AGENTS.md
├── Models/SignalEnvelope.cs
├── Models/SignalType.cs
├── Models/ReachabilitySignal.cs
├── Models/EntropySignal.cs
├── Models/ExploitabilitySignal.cs
├── Models/TrustSignal.cs
├── Models/UnknownSymbolSignal.cs
├── Abstractions/ISignalEmitter.cs
├── Abstractions/ISignalConsumer.cs
└── Extensions/ServiceCollectionExtensions.cs

src/Policy/__Libraries/StellaOps.Policy.Storage.Postgres/Repositories/
├── ISnapshotRepository.cs
├── SnapshotRepository.cs
├── IViolationEventRepository.cs
├── ViolationEventRepository.cs
├── IConflictRepository.cs
├── ConflictRepository.cs
├── ILedgerExportRepository.cs
└── LedgerExportRepository.cs

Files to Modify (5 files)

src/Policy/StellaOps.Policy.Engine/Program.cs
src/Policy/StellaOps.Policy.Engine/Telemetry/PolicyEngineTelemetry.cs
src/Policy/StellaOps.Policy.Engine/Endpoints/RiskSimulationEndpoints.cs
src/Policy/StellaOps.Policy.Engine/Endpoints/PathScopeSimulationEndpoint.cs
etc/policy-engine.yaml.sample

Success Criteria

  • Rate limiting returns 429 when quota exceeded
  • Signals library compiles and referenced by 5+ modules
  • All 5 repositories pass CRUD tests
  • Endpoints return proper responses with auth
  • Telemetry metrics visible in dashboards
  • No regression in existing tests

Risk Mitigation

Risk Mitigation
Breaking existing endpoints Feature flag rate limiting
Signal library circular deps Careful namespace isolation
Migration failures Test migrations in isolated DB first
Test flakiness Use deterministic test data

Next Steps

  1. Start Phase 1 - Implement rate limiting (simplest, immediate impact)
  2. Parallel Phase 2 - Create Signals.Contracts scaffolding
  3. Review - Get feedback before Phase 3