Files
git.stella-ops.org/docs/contracts/function-map-v1.md
2026-01-24 00:12:43 +02:00

7.0 KiB

Function Map V1 Contract

Predicate Type: https://stella.ops/predicates/function-map/v1 DSSE Payload Type: application/vnd.stellaops.function-map.v1+json Schema Version: 1.0.0

Overview

A function map predicate declares the expected call paths for a service component, enabling verification of runtime behavior against static analysis. It follows the in-toto attestation framework.


Predicate Schema

{
  "type": "https://stella.ops/predicates/function-map/v1",
  "subject": {
    "purl": "pkg:oci/my-service@sha256:abc123...",
    "digest": { "sha256": "abc123..." }
  },
  "predicate": {
    "service": "my-backend",
    "build_id": "build-456",
    "expected_paths": [...],
    "coverage": {
      "min_observation_rate": 0.95,
      "window_seconds": 1800,
      "fail_on_unexpected": false
    },
    "generated_at": "2026-01-23T10:00:00Z",
    "generated_from": {
      "sbom_ref": "oci://registry/sbom@sha256:...",
      "static_analysis_ref": "oci://registry/analysis@sha256:..."
    },
    "generator": {
      "name": "stella-cli",
      "version": "2.0.0",
      "commit": "abc123"
    }
  }
}

Subject

Field Type Required Description
purl string Yes Package URL of the subject artifact
digest object Yes Content digest (sha256, sha512, etc.)

Predicate Fields

Field Type Required Description
service string Yes Service name for correlation
build_id string No Build identifier for provenance correlation
expected_paths array Yes List of expected call paths
coverage object Yes Coverage thresholds for verification
generated_at ISO 8601 Yes Generation timestamp
generated_from object No Source references (SBOM, static analysis)
generator object No Tool that generated the predicate

Expected Path

Each expected path represents a call chain starting from an entrypoint:

{
  "path_id": "path-001",
  "entrypoint": {
    "symbol": "handleRequest",
    "node_hash": "sha256:..."
  },
  "expected_calls": [
    {
      "symbol": "crypto_sign",
      "purl": "pkg:deb/libcrypto3@3.0.0",
      "node_hash": "sha256:...",
      "probe_types": ["uprobe"],
      "optional": false,
      "function_address": null,
      "binary_path": "/usr/lib/libcrypto.so.3"
    }
  ],
  "path_hash": "sha256:...",
  "optional": false,
  "strict_ordering": false,
  "tags": ["crypto"]
}
Field Type Required Description
path_id string Yes Unique path identifier
entrypoint object Yes Path entry point (symbol + node_hash)
expected_calls array Yes List of expected function calls
path_hash string Yes SHA-256(entrypoint || sorted calls)
optional boolean No Whether this path is optional (default false)
strict_ordering boolean No Ordered sequence vs unordered set (default false)
tags array No Categorization tags (crypto, auth, network, etc.)

Expected Call

Field Type Required Description
symbol string Yes Function name (demangled)
purl string Yes Package URL of the component containing this function
node_hash string Yes SHA-256(PURL + normalized symbol)
probe_types array Yes Acceptable probe types for observation
optional boolean No Whether this call is optional (default false)
function_address string No Address hint for probe attachment
binary_path string No Binary path for uprobe attachment

Probe Types

Type Description
kprobe Kernel function entry
kretprobe Kernel function return
uprobe User-space function entry
uretprobe User-space function return
tracepoint Kernel tracepoint
usdt User-space statically defined tracing

Coverage Thresholds

Field Type Default Description
min_observation_rate double 0.95 Minimum fraction of paths that must be observed
window_seconds integer 1800 Observation window duration
fail_on_unexpected boolean false Whether unexpected symbols cause verification failure

Node Hash Recipe

Node hashes provide content-addressable identifiers for function calls, matching the Witness V1 convention:

node_hash = SHA-256(PURL + ":" + normalize(symbol))

Where normalize(symbol):

  1. Demangle C++/Rust symbols
  2. Strip leading underscores (platform convention)
  3. Lowercase the result
  4. Remove whitespace

Path Hash Recipe

path_hash = SHA-256(entrypoint.node_hash + ":" + sort(calls.map(c => c.node_hash)).join(":"))

The path hash is independent of call ordering (sorted) unless strict_ordering is true, in which case calls are not sorted before hashing.


Coverage Calculation Algorithm

total_required = count(paths where optional == false)
observed_required = count(paths where optional == false AND has_matching_observation)

observation_rate = observed_required / total_required
                 = 0.0 if total_required == 0

verified = observation_rate >= coverage.min_observation_rate

For each path, an observation "matches" when:

  • At least one observation has a node_hash matching any call in the path
  • The observation falls within the time window
  • The probe type is in the call's probe_types list

Verification Algorithm

VERIFY(predicate, observations, options):
  1. Filter observations to time window [now - window_seconds, now]
  2. For each required expected_path:
     a. For each expected_call in path:
        - Find observations matching node_hash AND probe_type
        - Mark call as "observed" if any match found
     b. Mark path as "covered" if entrypoint OR any call observed
  3. Compute observation_rate = covered_paths / required_paths
  4. Collect unexpected = observations not matching any expected call
  5. Collect missing = required calls with no matching observation
  6. verified = observation_rate >= min_observation_rate
                AND (NOT fail_on_unexpected OR unexpected.count == 0)
  7. Return result with breakdown, unexpected, missing

Media Types

Usage Media Type
Function map predicate application/vnd.stella.function-map+json
DSSE-signed predicate application/vnd.dsse+json
Observations application/x-ndjson
Verification report application/vnd.stella.verification-report+json

Observation Record (NDJSON)

Each line in an observations file:

{
  "observation_id": "obs-123",
  "node_hash": "sha256:...",
  "function_name": "crypto_sign",
  "probe_type": "uprobe",
  "observed_at": "2026-01-23T10:05:00Z",
  "observation_count": 42,
  "container_id": "abc123",
  "pod_name": "my-service-pod-xyz",
  "namespace": "production",
  "duration_microseconds": 150
}