Custody declaration facts
createCustodyDeclarationFacts() produces one validated object for later
machine-readable and human-readable custody outputs. It performs no network
request, signature, timestamping, persistence or rendering.
import { createCustodyDeclarationFacts } from "@dataprism/sdk/evidence";
const facts = createCustodyDeclarationFacts({
encoding: {
id: "reed-solomon-systematic-v1",
dataShards: 7,
parityShards: 3,
},
providerIds: ["aws-primary", "r2-primary", "gcp-primary", "azure-primary"],
});
facts.largestShareOfShardsAtOneProvider;
// { numerator: 3, denominator: 10 }
facts.ciphertextSlicesAtThatProvider;
// { numerator: 2, denominator: 7 }The two values are separate integer ratios. The API does not calculate a percentage. The provider order must be the same order used by the round-robin upload placement.
Placement conditions
Let k be the number of data shards, t the total number of shards, and n
the number of configured providers. Round-robin placement puts at most
ceil(t / n) shards at one provider.
| Display label | Condition | Returned field |
|---|---|---|
| Provider-loss availability condition | t - ceil(t / n) >= k | providerLossAvailabilityConditionHolds |
| No provider holds a quorum | ceil(t / n) <= k - 1 | noProviderHoldsQuorum |
The second condition only bounds the single-provider case. Display a false result as does not hold; do not label it “Confidentiality” or imply that passing it establishes confidentiality.
The supported reference configuration is 7+3 over five providers. Both
conditions hold, with a largest shard holding of 2 of 10. The four-provider
example above also passes both conditions, with a largest holding of 3 of 10.
In systematic mode both examples store 2 of 7 ciphertext slices uncoded at
the most-loaded provider. Dense-Cauchy stores 0 of 7 there.
The upload policy defaults to custodyPolicy: "attested"
and rejects either failed condition before writing. Callers still supply the
shard counts; the policy does not choose them. An explicit "relaxed" upload
does not turn a failed condition into a passing one.
Uncoded ciphertext slices
For reed-solomon-dense-cauchy-v1 with more than one data shard, the
ciphertext-slice numerator is zero because no output shard is an unchanged
ciphertext slice. With one data shard, the count instead reflects whether an
unchanged slice lands at the declared most-loaded provider. For example, 1+0
over one provider reports 1 of 1, and the no-provider-quorum condition fails.
A zero count is not a claim that a shard contains no information, and neither
encoding mode provides a threshold-secrecy or general confidentiality guarantee.
Both modes require dataShards + parityShards <= 256. Dense-Cauchy also requires
2 * dataShards + parityShards <= 256 so its row and column field elements stay
distinct. Unsupported configurations are rejected rather than described in
custody facts.
Bucket state
Omitting bucketInspections records { status: "unknown" }. It does not infer
provider settings from application configuration or infrastructure code.
When inspections are supplied, there must be one valid record for every
configured provider. The input contract is versioned as
dataprism.storage-configuration.v1. Pass the results from
inspectStorageProviders() directly to bucketInspections; no adapter or type
assertion is needed. Strict typechecks and integration tests cover this boundary.
Results are normalized into provider placement order.
Partial, unsupported and failed outcomes, unknown control states and bounded
limitations are retained rather than presented as successful observations.
A detected bucket state means that a valid inspection result was supplied for every configured provider. It does not mean that every provider returned a complete or successful result.
These are retained observations, not a live check. Keep the source, collection time and limitations with the displayed facts. A later provider configuration change does not update an existing declaration.
import { inspectStorageProviders } from "@dataprism/sdk/storage";
const bucketInspections = await inspectStorageProviders(providers);
const facts = createCustodyDeclarationFacts({
encoding,
providerIds: providers.map((provider) => provider.id),
bucketInspections,
});Here, providers must be in the upload's placement order and encoding must
describe its selected encoding mode and shard counts. An unavailable inspection
remains unsupported or failed in the resulting facts.
Evidence boundary
The object records deterministic placement arithmetic, the selected encoding identifier and supplied storage-control observations. The two booleans report whether the most-loaded provider can be lost while retaining a reconstruction quorum, and whether any single provider holds that quorum.
These facts do not establish deletion, confidentiality, threshold secrecy, provider honesty or an independently established time. Signing, RFC 3161 timestamping, JSON and PDF generation, persistence and registry anchoring are separate operations.
Location repair preserves the original content commitment and anchor time, and advances a separate manifest version only after verifying replacement shards. Stale updates cannot overwrite a newer manifest silently. These implemented lineage checks are separate from accepting measured gas and storage costs before deployment under #65.

