Skip to main content

structured-data attestor

Generic JSON-ingestion attestor: reads a recipe-pointed JSON product, canonicalizes it (RFC 8785 JCS) for a stable digest, selects in-toto subjects with an RFC 9535 JSONPath subset, and emits a signed envelope keyed on those subjects.

Namestructured-data
Predicate typehttps://aflock.ai/attestations/structured-data/v0.1
Lifecyclepostproduct
Default binary?No — builder opt-in only

What it captures​

The Predicate struct defines the signed payload:

JSON fieldGo typeSource
dataTypestringRecipe-supplied label for the data shape (free-form).
collectedAttime.TimeUTC timestamp at attestation time.
subjectQuerystringThe JSONPath used to select subjects.
subjectPrefixstring (omitempty)Optional prefix applied to each subject key.
subjectCountintNumber of subjects derived from the query.
datajson.RawMessage (omitempty)The full JCS-canonical bytes — only when embed-data is true.
dataDigeststringSHA-256 (hex) of the JCS-canonical encoding of the data file.
subjectDigestsmap[string]string (omitempty)Per-subject key → sha256(identity) map.
sourceFilestring (omitempty)Product path the bytes were read from.
sourceDigestcryptoutil.DigestSet (omitempty)Re-computed digest of the source file (verified against the product digest before reading).

When to use​

When you have JSON evidence from a source that doesn't have a dedicated attestor (Kratos admin-API responses, Splunk saved-search results, Snyk project exports, custom compliance API outputs) and shipping a bespoke attestor would be overkill. The combination of JCS canonicalization plus JSONPath subject selection lets policy join across attestations on stable identity values rather than file hashes.

Flags​

Configuration is supplied via package-level With* ConfigOptions (typically wired by a recipe):

OptionRequiredPurpose
WithDataFile(path)NoExplicit product path to attest over. If unset, the first product with a JSON MIME wins.
WithSubjectQuery(jsonpath)YesRFC 9535 JSONPath expression selecting subject identity values. Attest errors if empty.
WithSubjectPrefix(prefix)NoString prepended to each subject key.
WithDataType(label)NoRecipe-defined label written into dataType.
WithEmbedData(bool)NoWhen true, include the canonical bytes in predicate.data. Default false — only the digest is recorded.

Output shape​

{
"dataType": "kratos.identities.v1",
"collectedAt": "2026-05-21T14:02:11Z",
"subjectQuery": "$.identities[*].id",
"subjectPrefix": "kratos:identity:",
"subjectCount": 3,
"dataDigest": "9f2c…",
"subjectDigests": {
"kratos:identity:abc-123": "ba78…",
"kratos:identity:def-456": "1d44…"
},
"sourceFile": "kratos-identities.json",
"sourceDigest": { "sha256": "…" }
}

Gotchas​

  • JSON only. resolveDataFile matches products by strings.Contains(p.MimeType, "json"). YAML, XML, and plain-text products are not picked up — pre-convert to JSON in the recipe.
  • Subject identity is the path's value, not the path. Subjects are keyed subjectPrefix + stringifyIdentity(value) with digest = sha256(identity-string). This matches the convention shared with prowler, aws-config, asff, and steampipe so cross-attestation graph joins work.
  • Only scalar matches become subjects. Strings, JSON numbers (whole numbers render as integers, never scientific notation), and booleans ("true"/"false") are kept. Maps, arrays, and nulls are logged at debug and dropped.
  • Subject query is required. Calling Attest without WithSubjectQuery errors out — there is no default selector.
  • Source file is re-digested on read. The attestor recomputes the file digest under the context's hash list and compares it to the product's recorded digest; a mismatch errors with "concurrent write?".
  • Data is not embedded by default. Without WithEmbedData(true), only dataDigest is signed. Verifiers needing the raw values must keep the source file out-of-band or opt in.
  • Each envelope is exported standalone. Export() returns true, so per-recipe envelopes are addressable independently rather than merged into the parent Collection — designed for FedRAMP 20x per-KSI VSA consumption.

CLI example​

See the constraint summary + reproduction recipe at https://github.com/aflock-ai/attestor-compliance-examples/tree/main/32-structured-data. This attestor is currently blocked or doc-only — the linked example explains why and shows the recipe to validate once the constraint is removed.

See also​