7.0 KiB
Function Map V1 Contract
Predicate Type:
https://stella.ops/predicates/function-map/v1DSSE Payload Type:application/vnd.stellaops.function-map.v1+jsonSchema 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):
- Demangle C++/Rust symbols
- Strip leading underscores (platform convention)
- Lowercase the result
- 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_hashmatching any call in the path - The observation falls within the time window
- The probe type is in the call's
probe_typeslist
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
}