# Attribute Governance (SA)

Full detail lives in the "Attribute Governance" and "Egress Packing &
Signing Selection" sections of
`docs/superpowers/specs/2026-07-28-flowfile-packaging-signing-design.md`.

- **Placeholder naming until enumerated:** external attributes
  `SA-V3-Ext-01`…`SA-V3-Ext-15`, internal attributes `SA-V3-Int-01`…
  `SA-V3-Int-NN`. Real names are an open item. Placeholder regexes:
  external `^SA-V3-Ext-(0[1-9]|1[0-5])$`, internal `^SA-V3-Int-\d{2}$`.
- **Content hash algorithm is configurable, not fixed.** It's a dynamic
  property on the custom ingest processor (mirrored on egress), and the
  chosen algorithm is itself recorded as a controlled SA attribute so it's
  self-describing at verification time — no out-of-band assumption needed.
  This resolves what was previously an unspecified gap: earlier drafts
  named a signing algorithm (Ed25519) but never a content-hash algorithm.
- **~15 controlled external attributes** (`SA-V3-Ext-##`) are **owned by
  the external S3 handover interface spec**, not authored by this design —
  this design just consumes them. Only a subset of the ~15 is mandatory;
  the rest are optional (absence isn't an error). Eligible to carry
  through from ingest to final destination; which ones actually get
  packed/signed at a given egress point is a further, per-processor
  selection (see egress regex below), so "the attribute list" elsewhere
  means this controlled pool or a configured subset of it, not every
  FlowFile attribute.
- **The permissible read (input) and write (output) attribute sets are the
  same list** for external S3 — one shared contract, not independently-
  evolved read/write regexes. This resolves the write-side/read-side
  reconciliation concern in principle; still need the actual handover spec
  document to confirm exact key-casing and check for key collisions with
  this design's own fallback-flag/size attributes (open item).
- **Controlled internal attributes** (`SA-V3-Int-##`, count `NN` TBD) are
  a separate set, used for SA's own routing/processing **and** for
  **internal S3** usage: the reassembly/staging space
  (`staging/<generation-id>/<chunk-index>` objects) and the replay buffer
  (Replay DB's S3 tier for large files) are internal S3, distinct from
  external S3 (the handover-spec-governed delivery boundary). Internal
  attributes are never signed, never packed for external delivery — **but
  they do cross the MR hop.** MR bridges the two NiFi chains *within* SA;
  it is not SA's external boundary. Internal attributes are only stripped
  at true external egress (final S3/HTTP/filesystem delivery out of SA),
  not at MR, and not at internal S3 (which is inside the boundary).
  How internal attributes are represented on internal S3 objects
  (metadata/tags/JSON) is still open.
- **Import filter:** delete-attributes-by-regex, non-matches against the
  external allowlist regex dropped at ingest.
- **External-egress filter:** delete-attributes-by-regex using the
  **inverse** of the external allowlist regex — one step that strips
  internal (`SA-V3-Int-##`) attributes and anything else uncontrolled at
  once, without needing to separately enumerate "internal." Only
  controlled external attributes reach S3/HTTP/filesystem destinations.
- **Egress Regex property (write-side attribute selection):** each egress
  processor has a configurable Regex property naming which controlled
  external attributes get packed and signed at that point in the flow —
  deliberately per-processor so a flow author can configure different
  subsets at different flow positions. For S3, the same regex mechanism
  selects which matched attributes get written as `x-amz-meta-*` user
  metadata (when the matched set is under 2KB URL-encoded); attributes
  that don't fit go into the JSON manifest trailer instead, per the
  existing >2KB fallback path. This is a selection *within* the
  permissible external attribute set (see above) — the permissible set
  itself is shared between read and write, but which subset of it a given
  egress point actually packs/promotes to metadata is still this design's
  own per-processor choice.
- **Ingest-time signing is optional**, not mandatory — a configurable
  ingest-processor property. Egress-time signing (of the regex-selected
  subset) is what actually produces the signed artifact; ingest signing,
  when enabled, is just an earlier in-SA integrity checkpoint.
- **Ingest-time validation is conditional on presence, not optional
  itself:** unsigned data at ingest is fine (signing is optional). But if
  data arrives at ingest *already signed* (e.g. replay, or from an earlier
  stage), that signature must be validated on arrival — invalid signature
  routes to failure, same as any other corruption case.
- **S3 JSON-fallback flag always pairs with a size attribute:** whenever
  the >2KB fallback flag is set on an S3 object, this design also writes a
  second attribute giving the JSON trailer's byte size. Together they let
  a reader do one suffix-range `GET` (`Range: bytes=-N`) to fetch exactly
  the appended JSON and parse it into attributes — no footer read needed
  on S3 (footer remains the fallback mechanism for filesystem/HTTP, which
  lack a tag/metadata side-channel).

See [[packaging-signing-decisions]] and [[project-purpose]].
