TLPT Scope v1 Contract
Contract ID: CONTRACT-TLPT-SCOPE-V1-023 Version: tlpt-scope.v1 Status: Draft Owner: Tools Guild Last updated: 2026-05-01
Purpose
TLPT Scope v1 is the deterministic white-team scoping document for DORA TLPT preparation. It identifies the tenant, included critical assets, included business functions, third-party dependencies, TLPT objectives, ReachGraph subgraph reference, and frozen baseline hash used for later replay and evidence pack assembly. The frozen baseline hash is the baselineHash from tlpt-baseline.v1once Replay Core baseline capture has run.
This contract is for engineers and white-team auditors who scope a DORA TLPT engagement. The scope document is the first artifact of the TLPT evidence chain: scope → baseline → evidence pack.
The scoper does not run offensive tooling and does not perform red-team activity. It only produces evidence-side scope JSON from declared inputs.
Schema Pinning
- Schema version is pinned to
tlpt-scope.v1. - JSON schema:
docs/contracts/schemas/tlpt-scope-v1.schema.json. - Tool implementation:
src/Tools/StellaOps.Tools.TlptScoper/. - Deterministic fixture:
src/Tools/StellaOps.Tools.TlptScoper/fixtures/synthetic-tenant/. - Replay baseline contract:
docs/contracts/tlpt-baseline-v1.md. - Breaking changes require a new schema version; consumers must not auto-follow future TLPT scope schemas.
Document Shape
Required top-level fields:
schemaVersion:tlpt-scope.v1.tenantId: tenant being scoped.scopeId:tlpt-scope:<24 lowercase hex>. Deterministic identifier derived by SHA-256 over newline-joined material: the schema version literal (tlpt-scope.v1),tenantId, the normalized threshold, the frozen baseline hash, the ReachGraph subgraph digest,validFrom,validTo, then the sorted included asset ids. The id takes the 24 hex characters of that digest at positions 7-30 (skipping thesha256:prefix).scopeHash: SHA-256 over the canonical scope payload excludingscopeHash.criticalityThreshold:low,medium,high, orcritical.assetRegistry:asset-registry.v1source metadata and deterministic input hash.reachabilitySubgraph: ReachGraph source, digest, route, and optional local input hash.includedAssets[]: non-tombstoned tenant assets whose recognized criticality is greater than or equal to the threshold.includedFunctions[]: business functions derived from included assets of typeservice. Each function’sfunctionIdis the service asset id,nameresolves from the asset’sbusiness_function/function/service_name/nameattribute (falling back to the asset id), andsupportingAssetIdsis the asset’sdependsOnentries that resolve to active tenant assets.thirdPartyDependencies[]: included assets of typeintegrationorplugin, plusdependsOnentries of included assets that resolve to activeintegration/plugintenant assets. Eachproviderresolves from the asset’sprovider/nameattribute (falling back to the asset id); the emittedcriticalityis the asset criticality normalized for output, orunknownwhen the asset has no recognized criticality.objectives[]: deterministic white-team objectives, each with anobjectiveId(tlpt-objective:<16 lowercase hex>),type,title,assetRefs[], and the ReachGraph subgraph digest. Onefunction-coverageobjective is emitted per included function and onethird-party-dependencyobjective per third-party dependency; when neither exists but included assets do, a singleasset-set-coverageobjective covering all included asset ids is emitted instead. Objectives are sorted byobjectiveId.criticalityCoverage: total active (non-tombstoned) tenant assets, assessed assets (those with a recognized criticality), andcoveragePercentrounded to two decimals (away-from-zero);coveragePercentis100when there are zero active assets.warnings[]: deterministic, ordinally sorted warnings. Emitted when there are no active assets for the tenant, when criticality coverage is below 100% (Criticality coverage is N/M assets (P%).), and listing any asset ids missing a recognized criticality (Assets missing recognized criticality: ...).frozenBaselineHash: SHA-256baselineHashof thetlpt-baseline.v1baseline state this scope binds to.validFrom,validTo: UTC timestamps.
Determinism Rules
- Output JSON is canonical: sorted object properties, stable array ordering, no insignificant whitespace.
- Assets are ordered by
assetIdusing ordinal string ordering. - Dependency and supporting asset arrays are sorted and de-duplicated.
- Criticality values are normalized to lowercase.
- Timestamps are normalized to UTC
yyyy-MM-ddTHH:mm:ssZ. - The same inputs must produce the same
scopeId,scopeHash, and JSON bytes.
Input Source Rules
The deterministic local input path (--asset-registry <file>) remains supported for offline replay. It accepts a local asset-registry.v1 JSON input file containing a single asset object (with an assetId property), an array of assets, or an envelope with assets[] or items[]. The verbatim file bytes are SHA-256 hashed into assetRegistry.inputHash. Local scopes record assetRegistry.source as local-file.
Live Asset Registry query integration is selected by omitting --asset-registry and supplying --graph-asset-registry-url <base-url> (with an optional --graph-bearer-token). It targets Graph’s public Asset Registry client contract over POST /graph/assets/query (which requires the graph:read scope, or query equivalent, on the Graph service). The scoper sends the tenant via the X-StellaOps-TenantId request header, requests active assets (includeTombstones=false, limit=500, ordering=assetId), follows the returned cursor token until exhausted (failing closed on a repeated cursor), maps each AssetRegistryAssetRecord to the local asset-registry.v1 scope input shape, and hashes the canonical mapped asset set into assetRegistry.inputHash. Live scopes record assetRegistry.source as graph-api. If neither --asset-registry nor --graph-asset-registry-url is supplied, the command fails closed with exit code 2. Live-mode Graph query failures and any non-asset-registry.v1 response schema version also fail closed.
Offline and Replay Expectations
- No external network fetches are required in local mode.
- The Asset Registry input hash is recorded in
assetRegistry.inputHash. - The ReachGraph reference is supplied by
--reachability-subgraph-digest(an explicitsha256:<64 lowercase hex>content-addressed digest) and/or--reachability-subgraph <file>(a local subgraph file whose SHA-256 is recorded inreachabilitySubgraph.inputHash). When no explicit digest is given, the local file hash is used as the digest. At least one of the two must be provided, and the resolved digest must be a validsha256:digest, or the command fails closed.reachabilitySubgraph.sourceis the fixed literalsrc/ReachGraphandrouteis/v1/reachgraphs/<url-escaped-digest>. - The ReachGraph digest is recorded in every objective’s
reachabilitySubgraphDigestand inreachabilitySubgraph.digest. frozenBaselineHashis supplied from the Replay Core TLPT baseline producer when available; CLI wiring remains a separate TLPT command concern.- Later TLPT evidence packs must bind this scope document by
scopeHash.
Example Command
Local/offline mode. Canonical JSON is written to stdout unless --output/-o is supplied. Here the ReachGraph reference is derived from the subgraph file hash because --reachability-subgraph-digest is omitted.
dotnet run --project src/Tools/StellaOps.Tools.TlptScoper/StellaOps.Tools.TlptScoper.csproj -- scope `
--tenant tenant-a `
--criticality-threshold high `
--asset-registry src/Tools/StellaOps.Tools.TlptScoper/fixtures/synthetic-tenant/asset-registry.json `
--reachability-subgraph src/Tools/StellaOps.Tools.TlptScoper/fixtures/synthetic-tenant/reachgraph-subgraph.json `
--frozen-baseline-hash sha256:2222222222222222222222222222222222222222222222222222222222222222 `
--valid-from 2026-05-01T00:00:00Z `
--valid-to 2026-08-01T00:00:00Z `
--output scope.json
Live Graph mode. Omit --asset-registry and supply the Graph base URL; an explicit ReachGraph digest may replace the local subgraph file.
dotnet run --project src/Tools/StellaOps.Tools.TlptScoper/StellaOps.Tools.TlptScoper.csproj -- scope `
--tenant tenant-a `
--criticality-threshold high `
--graph-asset-registry-url https://graph.internal/ `
--graph-bearer-token <token> `
--reachability-subgraph-digest sha256:1111111111111111111111111111111111111111111111111111111111111111 `
--frozen-baseline-hash sha256:2222222222222222222222222222222222222222222222222222222222222222 `
--valid-from 2026-05-01T00:00:00Z `
--valid-to 2026-08-01T00:00:00Z
CLI options
| Option | Required | Notes |
|---|---|---|
--tenant | yes | Tenant id to scope. |
--criticality-threshold | yes | low, medium, high, or critical. |
--asset-registry | local mode | Local asset-registry.v1 JSON input. |
--graph-asset-registry-url | live mode | Base URL for live Graph Asset Registry; used only when --asset-registry is omitted. |
--graph-bearer-token | no | Optional bearer token for live Graph requests. |
--reachability-subgraph | one of these two | Local ReachGraph subgraph file (hashed into reachabilitySubgraph.inputHash). |
--reachability-subgraph-digest | one of these two | Explicit sha256: ReachGraph digest; falls back to the subgraph file hash when omitted. |
--frozen-baseline-hash | yes | sha256: baseline hash this scope binds to. |
--valid-from, --valid-to | yes | UTC RFC3339 timestamps; validTo must be later than validFrom. |
--output, -o | no | Output file; writes canonical JSON to stdout when omitted. |
