Files
git.stella-ops.org/docs/modules/signals/contracts/signals-provenance-contract.md
StellaOps Bot 5fc469ad98 feat: Add VEX Status Chip component and integration tests for reachability drift detection
- Introduced `VexStatusChipComponent` to display VEX status with color coding and tooltips.
- Implemented integration tests for reachability drift detection, covering various scenarios including drift detection, determinism, and error handling.
- Enhanced `ScannerToSignalsReachabilityTests` with a null implementation of `ICallGraphSyncService` for better test isolation.
- Updated project references to include the new Reachability Drift library.
2025-12-20 01:26:42 +02:00

11 KiB

Signals Provenance Contract v1.0.0

Status: APPROVED Version: 1.0.0 Effective: 2025-12-19 Owner: Signals Guild + Platform Storage Guild Sprint: SPRINT_0140_0001_0001 (unblocks SIGNALS-24-002, 24-003, 24-004, 24-005)


1. Purpose

This contract defines the provenance tracking for runtime facts, callgraph storage, and CAS (Content-Addressable Storage) promotion policies. It enables deterministic, auditable signal processing with signed manifests and attestations.

2. Schema References

Schema Location
Provenance Feed docs/schemas/provenance-feed.schema.json
Runtime Facts docs/signals/runtime-facts.md
Reachability Input docs/modules/policy/contracts/reachability-input-contract.md

3. CAS Storage Architecture

3.1 Bucket Structure

cas://signals/
├── callgraphs/
│   ├── {tenant}/
│   │   ├── {graph_id}.ndjson.zst      # Compressed callgraph
│   │   └── {graph_id}.meta.json       # Callgraph metadata
│   └── global/
│       └── ...
├── manifests/
│   ├── {graph_id}.json                # Signed manifest
│   └── {graph_id}.json.dsse           # DSSE envelope
├── runtime-facts/
│   ├── {tenant}/
│   │   ├── {batch_id}.ndjson.zst      # Runtime fact batch
│   │   └── {batch_id}.provenance.json # Provenance record
│   └── global/
│       └── ...
└── attestations/
    └── {batch_id}.dsse                 # Batch attestation

3.2 Access Policies

Principal callgraphs manifests runtime-facts attestations
Signals Service read/write read/write read/write read/write
Policy Engine read read read read
Scanner Worker write - - -
Audit Service read read read read
All Others deny deny deny deny

3.3 Retention Policies

Content Type Retention GC Policy
Manifests Indefinite Never delete
Callgraphs (referenced) Indefinite Never delete
Callgraphs (orphan) 30 days Rolling GC
Runtime Facts 90 days Rolling GC
Attestations Indefinite Never delete

4. Manifest Schema

4.1 CallgraphManifest

public sealed record CallgraphManifest
{
    /// <summary>Unique graph identifier (ULID).</summary>
    public required string GraphId { get; init; }

    /// <summary>SHA-256 digest of callgraph content.</summary>
    public required string Digest { get; init; }

    /// <summary>Programming language.</summary>
    public required string Language { get; init; }

    /// <summary>Source identifier (scanner, analyzer, runtime agent).</summary>
    public required string Source { get; init; }

    /// <summary>When the callgraph was created.</summary>
    public required DateTimeOffset CreatedAt { get; init; }

    /// <summary>Tenant scope.</summary>
    public required string TenantId { get; init; }

    /// <summary>Component PURL.</summary>
    public required string ComponentPurl { get; init; }

    /// <summary>Entry points discovered.</summary>
    public ImmutableArray<string> EntryPoints { get; init; }

    /// <summary>Node count in the graph.</summary>
    public int NodeCount { get; init; }

    /// <summary>Edge count in the graph.</summary>
    public int EdgeCount { get; init; }

    /// <summary>Signing key ID.</summary>
    public string? SignerKeyId { get; init; }

    /// <summary>Signature (Base64).</summary>
    public string? Signature { get; init; }

    /// <summary>Rekor log UUID if transparency-logged.</summary>
    public string? RekorUuid { get; init; }
}

4.2 JSON Example

{
  "graphId": "01HWXYZ123456789ABCDEFGHJK",
  "digest": "sha256:7d9cd5f1a2a0dd9a41a2c43a5b7d8a0bcd9e34cf39b3f43a70595c834f0a4aee",
  "language": "javascript",
  "source": "stella-callgraph-node",
  "createdAt": "2025-12-19T10:00:00Z",
  "tenantId": "tenant-001",
  "componentPurl": "pkg:npm/%40acme/backend@1.2.3",
  "entryPoints": ["src/index.js", "src/server.js"],
  "nodeCount": 1523,
  "edgeCount": 4892,
  "signerKeyId": "signals-signer-2025-001",
  "signature": "base64...",
  "rekorUuid": "24296fb24b8ad77a..."
}

5. Runtime Facts Provenance

5.1 ProvenanceRecord

public sealed record RuntimeFactProvenance
{
    /// <summary>Provenance record ID (ULID).</summary>
    public required string ProvenanceId { get; init; }

    /// <summary>Callgraph ID this fact batch relates to.</summary>
    public required string CallgraphId { get; init; }

    /// <summary>Batch ID for this fact set.</summary>
    public required string BatchId { get; init; }

    /// <summary>When facts were ingested.</summary>
    public required DateTimeOffset IngestedAt { get; init; }

    /// <summary>When facts were received from source.</summary>
    public required DateTimeOffset ReceivedAt { get; init; }

    /// <summary>Tenant scope.</summary>
    public required string TenantId { get; init; }

    /// <summary>Source host/service.</summary>
    public required string Source { get; init; }

    /// <summary>Pipeline version (git SHA or build ID).</summary>
    public required string PipelineVersion { get; init; }

    /// <summary>SHA-256 of raw fact blob.</summary>
    public required string ProvenanceHash { get; init; }

    /// <summary>Signing key ID.</summary>
    public string? SignerKeyId { get; init; }

    /// <summary>Rekor UUID or skip reason.</summary>
    public string? RekorUuid { get; init; }

    /// <summary>Skip reason if not transparency-logged.</summary>
    public string? SkipReason { get; init; }

    /// <summary>Fact count in this batch.</summary>
    public int FactCount { get; init; }

    /// <summary>Fact types included.</summary>
    public ImmutableArray<string> FactTypes { get; init; }
}

5.2 Enrichment Pipeline

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│ Runtime Agent   │────▶│ Signals Ingest   │────▶│ CAS Storage     │
│ (runtime-facts) │     │ (provenance)     │     │ (facts+prov)    │
└─────────────────┘     └──────────────────┘     └─────────────────┘
                               │
                               ▼
                        ┌──────────────────┐
                        │ DSSE Attestation │
                        │ (per batch)      │
                        └──────────────────┘

6. API Endpoints

6.1 Callgraph Management

Endpoint Method Description
POST /signals/callgraphs POST Store new callgraph
GET /signals/callgraphs/{graphId} GET Retrieve callgraph
GET /signals/callgraphs/{graphId}/manifest GET Get signed manifest
GET /signals/callgraphs/by-purl/{purl} GET Find by component PURL

6.2 Runtime Facts

Endpoint Method Description
POST /signals/runtime-facts POST Ingest runtime fact batch
GET /signals/runtime-facts/{batchId} GET Retrieve fact batch
GET /signals/runtime-facts/{batchId}/provenance GET Get provenance record
GET /signals/runtime-facts/ndjson GET Stream facts (with provenance)

6.3 Query Parameters

Parameter Type Description
tenant string Filter by tenant
callgraph_id string Filter by callgraph
since datetime Facts after timestamp
include_provenance bool Include provenance_hash and callgraph_id

7. Signing and Attestation

7.1 Manifest Signing

All callgraph manifests are signed using:

  • Algorithm: ECDSA-P256-SHA256 or Ed25519
  • Key management: Via Authority service key registry
  • Transparency: Optional Sigstore Rekor logging
public interface IManifestSigner
{
    Task<SignedManifest> SignAsync(
        CallgraphManifest manifest,
        CancellationToken cancellationToken = default);

    Task<bool> VerifyAsync(
        SignedManifest signedManifest,
        CancellationToken cancellationToken = default);
}

7.2 Batch Attestation

Runtime fact batches are attested using in-toto/DSSE:

public sealed record RuntimeFactAttestation
{
    public required string PredicateType { get; init; } // "https://stella.ops/attestation/runtime-facts/v1"
    public required string BatchId { get; init; }
    public required string ProvenanceHash { get; init; }
    public required int FactCount { get; init; }
    public required DateTimeOffset Timestamp { get; init; }
    public required ImmutableArray<string> Subjects { get; init; } // callgraph IDs
}

8. Telemetry

8.1 Metrics

Metric Type Labels Description
signals_callgraphs_stored_total counter language, tenant Callgraphs stored
signals_callgraph_nodes_total histogram language Nodes per callgraph
signals_runtime_facts_ingested_total counter fact_type, tenant Facts ingested
signals_runtime_facts_batch_size histogram - Facts per batch
signals_provenance_records_total counter - Provenance records created
signals_attestations_created_total counter - DSSE attestations created
signals_cas_operations_total counter operation, result CAS operations

8.2 Alerts

groups:
  - name: signals-provenance
    rules:
      - alert: SignalsAttestationFailure
        expr: increase(signals_attestations_created_total{result="failure"}[5m]) > 0
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "Runtime fact attestation failures detected"

      - alert: SignalsProvenanceMissing
        expr: signals_runtime_facts_ingested_total - signals_provenance_records_total > 100
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Runtime facts missing provenance records"

9. Configuration

# etc/signals.yaml
Signals:
  CAS:
    BucketPrefix: "cas://signals"
    WriteEnabled: true
    RetentionDays:
      RuntimeFacts: 90
      OrphanCallgraphs: 30

  Provenance:
    Enabled: true
    SignManifests: true
    AttestBatches: true
    RekorEnabled: true  # Set to false for air-gap

  Signing:
    KeyId: "signals-signer-2025-001"
    Algorithm: "ECDSA-P256-SHA256"

10. Validation Rules

  1. GraphId must be valid ULID
  2. Digest must be valid sha256: prefixed hex
  3. Language must be known language identifier
  4. TenantId must exist in Authority tenant registry
  5. ComponentPurl must be valid Package URL
  6. ProvenanceHash must match recomputed hash of fact blob
  7. Manifests must have valid signature if SignManifests: true
  8. Attestations must have valid DSSE envelope

Changelog

Version Date Changes
1.0.0 2025-12-19 Initial release - unblocks SIGNALS-24-002 through 24-005