//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `rooms/blobs/upload/begin`. Version: `0.2`.
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
/**
What a party presents to act on a room. Carries the whole authority chain: a host MUST NOT dereference an authority credential's `parent` to fetch a link it was not given. Resolving over the network would make verification depend on availability, turn every identifier into a request the host can be induced to make against an address the holder chooses, and signal credential use to whoever hosts the identifier. A host MUST bind the presenter to the chain's leaf. A chain that verifies is evidence that authority was conferred on somebody; it is not evidence that the party presenting it is that somebody. The leaf's subject MUST equal the party the host authenticated for this request — an identity the transport established or a document `proof` proved, never one named in a payload. A host that omits this check authorizes every captured presentation, and the omission is silent, because the chain still verifies.
**Every credential is its issuer's attestation, and only its issuer's.** For the membership credential and for every link of `authority`, a host MUST verify the credential's `proof` with a key that the credential's own `issuer` lists for `assertionMethod` in its DID document, and MUST refuse a credential whose proof names a verification method that is not its issuer's. A proof that verifies under some other party's key is that party's statement, not the issuer's, and confers nothing. The same binding holds for every other document a room signs, such as a `RevocationList`.
**A chain that verifies may still be revoked.** A host that has accepted a `RevocationList` for the room (`rooms/revocations/put`) MUST, from the moment it accepted it, refuse every presentation in which the membership credential or any link of `authority` is revoked by that list, under every task and every version of every task it serves for the room. A chain from which any link is revoked is refused whole: a credential delegated from a revoked one confers nothing, however it was attenuated. The refusal is the same `permissionDenied` as any other failed presentation.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "AuthorityPresentation",
/// "description": "What a party presents to act on a room. Carries the whole authority chain: a host MUST NOT dereference an authority credential's `parent` to fetch a link it was not given. Resolving over the network would make verification depend on availability, turn every identifier into a request the host can be induced to make against an address the holder chooses, and signal credential use to whoever hosts the identifier. A host MUST bind the presenter to the chain's leaf. A chain that verifies is evidence that authority was conferred on somebody; it is not evidence that the party presenting it is that somebody. The leaf's subject MUST equal the party the host authenticated for this request — an identity the transport established or a document `proof` proved, never one named in a payload. A host that omits this check authorizes every captured presentation, and the omission is silent, because the chain still verifies.\n\n**Every credential is its issuer's attestation, and only its issuer's.** For the membership credential and for every link of `authority`, a host MUST verify the credential's `proof` with a key that the credential's own `issuer` lists for `assertionMethod` in its DID document, and MUST refuse a credential whose proof names a verification method that is not its issuer's. A proof that verifies under some other party's key is that party's statement, not the issuer's, and confers nothing. The same binding holds for every other document a room signs, such as a `RevocationList`.\n\n**A chain that verifies may still be revoked.** A host that has accepted a `RevocationList` for the room (`rooms/revocations/put`) MUST, from the moment it accepted it, refuse every presentation in which the membership credential or any link of `authority` is revoked by that list, under every task and every version of every task it serves for the room. A chain from which any link is revoked is refused whole: a credential delegated from a revoked one confers nothing, however it was attenuated. The refusal is the same `permissionDenied` as any other failed presentation.",
/// "type": "object",
/// "required": [
/// "authority",
/// "membership"
/// ],
/// "properties": {
/// "authority": {
/// "description": "The authority chain, LEAF FIRST: the first element is the credential being relied on and the last MUST be one issued by the room itself. Every link the presenter relies on is present, because the host will not fetch one. Capped at 8: verification is linear in chain length and runs on every operation, so an unbounded chain is a denial-of-service surface against the host. The known uses need 2 to 3 — a person attenuating to an agent, and that agent to a sub-agent.",
/// "type": "array",
/// "items": {
/// "type": "string"
/// },
/// "maxItems": 8,
/// "minItems": 1
/// },
/// "membership": {
/// "description": "The presenter's membership credential for this room, or — on a `private` room — a zero-knowledge presentation of it. Serialized per the governing profile.",
/// "type": "string"
/// },
/// "subjectBinding": {
/// "description": "REQUIRED on a `private` room, where the subject identifier is withheld: a proof that the membership credential and the authority chain's leaf describe the SAME subject. Without it two parties pool credentials — one contributes membership, the other authority — and the combination verifies as a single party holding both. A host MUST refuse a private-room presentation that omits this.",
/// "type": "string"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct AuthorityPresentation {
///The authority chain, LEAF FIRST: the first element is the credential being relied on and the last MUST be one issued by the room itself. Every link the presenter relies on is present, because the host will not fetch one. Capped at 8: verification is linear in chain length and runs on every operation, so an unbounded chain is a denial-of-service surface against the host. The known uses need 2 to 3 — a person attenuating to an agent, and that agent to a sub-agent.
pub authority: ::std::vec::Vec<::std::string::String>,
///The presenter's membership credential for this room, or — on a `private` room — a zero-knowledge presentation of it. Serialized per the governing profile.
pub membership: ::std::string::String,
///REQUIRED on a `private` room, where the subject identifier is withheld: a proof that the membership credential and the authority chain's leaf describe the SAME subject. Without it two parties pool credentials — one contributes membership, the other authority — and the combination verifies as a single party holding both. A host MUST refuse a private-room presentation that omits this.
#[serde(
rename = "subjectBinding",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub subject_binding: ::std::option::Option<::std::string::String>,
}
impl AuthorityPresentation {
pub fn builder() -> builder::AuthorityPresentation {
Default::default()
}
}
/**
The ciphertext manifest of one blob, committed by the uploader before any byte moves. Its digest is the BlobRef. Every chunk is checked against `chunks.chunkDigests` on arrival and the reassembled blob against `digest`, so a host can neither accept nor serve bytes the uploader did not commit to. The manifest also appears, by its BlobRef, inside the sealed FileManifest the author signs; a reader who opens the record therefore knows which ciphertext its author meant, and a host cannot substitute a different blob under the same record.
Consistency rules JSON Schema cannot state: `chunks.chunkCount` MUST equal ceil(size / chunks.chunkSize), and `chunks.chunkDigests` MUST have exactly `chunkCount` items. A party receiving a manifest violating either MUST refuse it.
Each transfer chunk is exactly one sealed STREAM segment (see FileManifest): every chunk but the last is `chunkSize` bytes, and `chunkSize` is the plaintext segment size plus the 16-byte AEAD tag. The 262144-byte ceiling of ChunkSize therefore puts the largest plaintext segment at 262128 bytes, and the 4096-chunk ceiling of ChunkCount puts the largest blob at 1 GiB of ciphertext.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "BlobManifest",
/// "description": "The ciphertext manifest of one blob, committed by the uploader before any byte moves. Its digest is the BlobRef. Every chunk is checked against `chunks.chunkDigests` on arrival and the reassembled blob against `digest`, so a host can neither accept nor serve bytes the uploader did not commit to. The manifest also appears, by its BlobRef, inside the sealed FileManifest the author signs; a reader who opens the record therefore knows which ciphertext its author meant, and a host cannot substitute a different blob under the same record.\n\nConsistency rules JSON Schema cannot state: `chunks.chunkCount` MUST equal ceil(size / chunks.chunkSize), and `chunks.chunkDigests` MUST have exactly `chunkCount` items. A party receiving a manifest violating either MUST refuse it.\n\nEach transfer chunk is exactly one sealed STREAM segment (see FileManifest): every chunk but the last is `chunkSize` bytes, and `chunkSize` is the plaintext segment size plus the 16-byte AEAD tag. The 262144-byte ceiling of ChunkSize therefore puts the largest plaintext segment at 262128 bytes, and the 4096-chunk ceiling of ChunkCount puts the largest blob at 1 GiB of ciphertext.",
/// "type": "object",
/// "required": [
/// "chunks",
/// "digest",
/// "size"
/// ],
/// "properties": {
/// "chunks": {
/// "$ref": "#/definitions/ChunkManifest"
/// },
/// "digest": {
/// "description": "Digest of the whole ciphertext, over its bytes. Checked after reassembly, so a correct set of chunks in the wrong order is still caught.",
/// "$ref": "#/definitions/DigestMultibase"
/// },
/// "size": {
/// "description": "Ciphertext bytes, padding included. What the host stores, and what every limit and usage figure is stated in.",
/// "$ref": "#/definitions/ExpectedSizeBytes"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct BlobManifest {
pub chunks: ChunkManifest,
///Digest of the whole ciphertext, over its bytes. Checked after reassembly, so a correct set of chunks in the wrong order is still caught.
pub digest: DigestMultibase,
///Ciphertext bytes, padding included. What the host stores, and what every limit and usage figure is stated in.
pub size: ExpectedSizeBytes,
}
impl BlobManifest {
pub fn builder() -> builder::BlobManifest {
Default::default()
}
}
///Number of chunks in the bundle, equal to ceil(expectedSizeBytes / chunkSize). Bounded at 4096 so that the manifest itself — one digest per chunk — fits in the single document that carries it under the same message-size reasoning as a chunk.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ChunkCount",
/// "description": "Number of chunks in the bundle, equal to ceil(expectedSizeBytes / chunkSize). Bounded at 4096 so that the manifest itself — one digest per chunk — fits in the single document that carries it under the same message-size reasoning as a chunk.",
/// "type": "integer",
/// "maximum": 4096.0,
/// "minimum": 1.0
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct ChunkCount(pub ::std::num::NonZeroU64);
impl ::std::ops::Deref for ChunkCount {
type Target = ::std::num::NonZeroU64;
fn deref(&self) -> &::std::num::NonZeroU64 {
&self.0
}
}
impl ::std::convert::From<ChunkCount> for ::std::num::NonZeroU64 {
fn from(value: ChunkCount) -> Self {
value.0
}
}
impl ::std::convert::From<::std::num::NonZeroU64> for ChunkCount {
fn from(value: ::std::num::NonZeroU64) -> Self {
Self(value)
}
}
impl ::std::str::FromStr for ChunkCount {
type Err = <::std::num::NonZeroU64 as ::std::str::FromStr>::Err;
fn from_str(value: &str) -> ::std::result::Result<Self, Self::Err> {
Ok(Self(value.parse()?))
}
}
impl ::std::convert::TryFrom<&str> for ChunkCount {
type Error = <::std::num::NonZeroU64 as ::std::str::FromStr>::Err;
fn try_from(value: &str) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::convert::TryFrom<String> for ChunkCount {
type Error = <::std::num::NonZeroU64 as ::std::str::FromStr>::Err;
fn try_from(value: String) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::fmt::Display for ChunkCount {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
self.0.fmt(f)
}
}
///Zero-based position of a chunk within its bundle. Valid values are 0 to chunkCount − 1; an index outside that range is refused with chunkOutOfRange rather than validated here, because the bound depends on the bundle.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ChunkIndex",
/// "description": "Zero-based position of a chunk within its bundle. Valid values are 0 to chunkCount − 1; an index outside that range is refused with chunkOutOfRange rather than validated here, because the bound depends on the bundle.",
/// "type": "integer",
/// "maximum": 4095.0,
/// "minimum": 0.0
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct ChunkIndex(pub i64);
impl ::std::ops::Deref for ChunkIndex {
type Target = i64;
fn deref(&self) -> &i64 {
&self.0
}
}
impl ::std::convert::From<ChunkIndex> for i64 {
fn from(value: ChunkIndex) -> Self {
value.0
}
}
impl ::std::convert::From<i64> for ChunkIndex {
fn from(value: i64) -> Self {
Self(value)
}
}
impl ::std::str::FromStr for ChunkIndex {
type Err = <i64 as ::std::str::FromStr>::Err;
fn from_str(value: &str) -> ::std::result::Result<Self, Self::Err> {
Ok(Self(value.parse()?))
}
}
impl ::std::convert::TryFrom<&str> for ChunkIndex {
type Error = <i64 as ::std::str::FromStr>::Err;
fn try_from(value: &str) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::convert::TryFrom<String> for ChunkIndex {
type Error = <i64 as ::std::str::FromStr>::Err;
fn try_from(value: String) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::fmt::Display for ChunkIndex {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
self.0.fmt(f)
}
}
/**
The terms of a `chunkedTrustTask` transfer, committed before any chunk moves. On export the recipient states them in the descriptor; on import the producer pre-commits them in the request and the recipient echoes them. Either way the manifest arrives in a document whose proof is REQUIRED, so the per-chunk digests are authenticated by the party that computed them and each chunk can be verified — and a single bad chunk re-fetched or refused — on arrival rather than only after reassembly.
Consistency rules JSON Schema cannot state: `chunkCount` MUST equal ceil(expectedSizeBytes / chunkSize) for the bundle the manifest describes, and `chunkDigests` MUST have exactly `chunkCount` items. A party receiving a manifest violating either MUST refuse it.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ChunkManifest",
/// "description": "The terms of a `chunkedTrustTask` transfer, committed before any chunk moves. On export the recipient states them in the descriptor; on import the producer pre-commits them in the request and the recipient echoes them. Either way the manifest arrives in a document whose proof is REQUIRED, so the per-chunk digests are authenticated by the party that computed them and each chunk can be verified — and a single bad chunk re-fetched or refused — on arrival rather than only after reassembly.\n\nConsistency rules JSON Schema cannot state: `chunkCount` MUST equal ceil(expectedSizeBytes / chunkSize) for the bundle the manifest describes, and `chunkDigests` MUST have exactly `chunkCount` items. A party receiving a manifest violating either MUST refuse it.",
/// "type": "object",
/// "required": [
/// "chunkCount",
/// "chunkDigests",
/// "chunkSize"
/// ],
/// "properties": {
/// "chunkCount": {
/// "$ref": "#/definitions/ChunkCount"
/// },
/// "chunkDigests": {
/// "description": "Digest of each chunk's raw bytes (not of its base64url encoding), in index order. Compared as decoded multihash bytes, never as encoded strings. sha2-256 is RECOMMENDED and MUST be implemented by every party; a party that does not implement the hash a digest names MUST treat the manifest as unverifiable rather than skip the check.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/DigestMultibase"
/// },
/// "maxItems": 4096,
/// "minItems": 1
/// },
/// "chunkSize": {
/// "$ref": "#/definitions/ChunkSize"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct ChunkManifest {
#[serde(rename = "chunkCount")]
pub chunk_count: ChunkCount,
///Digest of each chunk's raw bytes (not of its base64url encoding), in index order. Compared as decoded multihash bytes, never as encoded strings. sha2-256 is RECOMMENDED and MUST be implemented by every party; a party that does not implement the hash a digest names MUST treat the manifest as unverifiable rather than skip the check.
#[serde(rename = "chunkDigests")]
pub chunk_digests: ::std::vec::Vec<DigestMultibase>,
#[serde(rename = "chunkSize")]
pub chunk_size: ChunkSize,
}
impl ChunkManifest {
pub fn builder() -> builder::ChunkManifest {
Default::default()
}
}
///Size in bytes of every chunk except the last, which carries the remainder and is between 1 and this value inclusive. The ceiling of 262144 (256 KiB) is normative and is derived in `vta/backup/initiate-export/1.1` under Chunked transfer: it is the largest power of two whose `get-chunk` or `put-chunk` document still fits a 1 MiB mediator message after base64url encoding of `data`, DIDComm authcrypt encoding, and two nested forward wrappers. The floor of 16384 keeps a 1 GiB bundle within `ChunkCount`'s ceiling at sizes a constrained transport can still choose.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ChunkSize",
/// "description": "Size in bytes of every chunk except the last, which carries the remainder and is between 1 and this value inclusive. The ceiling of 262144 (256 KiB) is normative and is derived in `vta/backup/initiate-export/1.1` under Chunked transfer: it is the largest power of two whose `get-chunk` or `put-chunk` document still fits a 1 MiB mediator message after base64url encoding of `data`, DIDComm authcrypt encoding, and two nested forward wrappers. The floor of 16384 keeps a 1 GiB bundle within `ChunkCount`'s ceiling at sizes a constrained transport can still choose.",
/// "type": "integer",
/// "maximum": 262144.0,
/// "minimum": 16384.0
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct ChunkSize(pub i64);
impl ::std::ops::Deref for ChunkSize {
type Target = i64;
fn deref(&self) -> &i64 {
&self.0
}
}
impl ::std::convert::From<ChunkSize> for i64 {
fn from(value: ChunkSize) -> Self {
value.0
}
}
impl ::std::convert::From<i64> for ChunkSize {
fn from(value: i64) -> Self {
Self(value)
}
}
impl ::std::str::FromStr for ChunkSize {
type Err = <i64 as ::std::str::FromStr>::Err;
fn from_str(value: &str) -> ::std::result::Result<Self, Self::Err> {
Ok(Self(value.parse()?))
}
}
impl ::std::convert::TryFrom<&str> for ChunkSize {
type Error = <i64 as ::std::str::FromStr>::Err;
fn try_from(value: &str) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::convert::TryFrom<String> for ChunkSize {
type Error = <i64 as ::std::str::FromStr>::Err;
fn try_from(value: String) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::fmt::Display for ChunkSize {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
self.0.fmt(f)
}
}
/**
A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.
Multihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.
This definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.
Restricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that "interoperability is not guaranteed between implementations using such values", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DigestMultibase",
/// "description": "A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\n\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\n\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\n\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \"interoperability is not guaranteed between implementations using such values\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.",
/// "examples": [
/// "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
/// ],
/// "type": "string",
/// "minLength": 16,
/// "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DigestMultibase(::std::string::String);
impl ::std::ops::Deref for DigestMultibase {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DigestMultibase> for ::std::string::String {
fn from(value: DigestMultibase) -> Self {
value.0
}
}
impl ::std::str::FromStr for DigestMultibase {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 16usize {
return Err("shorter than 16 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\"".into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DigestMultibase {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DigestMultibase {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Total byte count of the bundle. A zero-length bundle is not a degenerate success — nothing was serialized — so the floor is 1.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ExpectedSizeBytes",
/// "description": "Total byte count of the bundle. A zero-length bundle is not a degenerate success — nothing was serialized — so the floor is 1.",
/// "type": "integer",
/// "minimum": 1.0
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct ExpectedSizeBytes(pub ::std::num::NonZeroU64);
impl ::std::ops::Deref for ExpectedSizeBytes {
type Target = ::std::num::NonZeroU64;
fn deref(&self) -> &::std::num::NonZeroU64 {
&self.0
}
}
impl ::std::convert::From<ExpectedSizeBytes> for ::std::num::NonZeroU64 {
fn from(value: ExpectedSizeBytes) -> Self {
value.0
}
}
impl ::std::convert::From<::std::num::NonZeroU64> for ExpectedSizeBytes {
fn from(value: ::std::num::NonZeroU64) -> Self {
Self(value)
}
}
impl ::std::str::FromStr for ExpectedSizeBytes {
type Err = <::std::num::NonZeroU64 as ::std::str::FromStr>::Err;
fn from_str(value: &str) -> ::std::result::Result<Self, Self::Err> {
Ok(Self(value.parse()?))
}
}
impl ::std::convert::TryFrom<&str> for ExpectedSizeBytes {
type Error = <::std::num::NonZeroU64 as ::std::str::FromStr>::Err;
fn try_from(value: &str) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::convert::TryFrom<String> for ExpectedSizeBytes {
type Error = <::std::num::NonZeroU64 as ::std::str::FromStr>::Err;
fn try_from(value: String) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::fmt::Display for ExpectedSizeBytes {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
self.0.fmt(f)
}
}
///After which the bundle is collected: staged bytes discarded, tokens and chunk requests refused. Short by design. For a chunked transfer a recipient may move it later as chunks are exchanged, never past its own ceiling — each chunk response reports the current value.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ExpiresAt",
/// "description": "After which the bundle is collected: staged bytes discarded, tokens and chunk requests refused. Short by design. For a chunked transfer a recipient may move it later as chunks are exchanged, never past its own ceiling — each chunk response reports the current value.",
/// "type": "string",
/// "format": "date-time"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct ExpiresAt(pub ::chrono::DateTime<::chrono::offset::Utc>);
impl ::std::ops::Deref for ExpiresAt {
type Target = ::chrono::DateTime<::chrono::offset::Utc>;
fn deref(&self) -> &::chrono::DateTime<::chrono::offset::Utc> {
&self.0
}
}
impl ::std::convert::From<ExpiresAt> for ::chrono::DateTime<::chrono::offset::Utc> {
fn from(value: ExpiresAt) -> Self {
value.0
}
}
impl ::std::convert::From<::chrono::DateTime<::chrono::offset::Utc>> for ExpiresAt {
fn from(value: ::chrono::DateTime<::chrono::offset::Utc>) -> Self {
Self(value)
}
}
impl ::std::str::FromStr for ExpiresAt {
type Err = <::chrono::DateTime<::chrono::offset::Utc> as ::std::str::FromStr>::Err;
fn from_str(value: &str) -> ::std::result::Result<Self, Self::Err> {
Ok(Self(value.parse()?))
}
}
impl ::std::convert::TryFrom<&str> for ExpiresAt {
type Error = <::chrono::DateTime<::chrono::offset::Utc> as ::std::str::FromStr>::Err;
fn try_from(value: &str) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::convert::TryFrom<String> for ExpiresAt {
type Error = <::chrono::DateTime<::chrono::offset::Utc> as ::std::str::FromStr>::Err;
fn try_from(value: String) -> ::std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl ::std::fmt::Display for ExpiresAt {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
self.0.fmt(f)
}
}
///Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ext",
/// "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
/// "type": "object",
/// "minProperties": 1,
/// "additionalProperties": true,
/// "propertyNames": {
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Ext(pub ::std::collections::HashMap<ExtKey, ::serde_json::Value>);
impl ::std::ops::Deref for Ext {
type Target = ::std::collections::HashMap<ExtKey, ::serde_json::Value>;
fn deref(&self) -> &::std::collections::HashMap<ExtKey, ::serde_json::Value> {
&self.0
}
}
impl ::std::convert::From<Ext> for ::std::collections::HashMap<ExtKey, ::serde_json::Value> {
fn from(value: Ext) -> Self {
value.0
}
}
impl ::std::convert::From<::std::collections::HashMap<ExtKey, ::serde_json::Value>> for Ext {
fn from(value: ::std::collections::HashMap<ExtKey, ::serde_json::Value>) -> Self {
Self(value)
}
}
///`ExtKey`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExtKey(::std::string::String);
impl ::std::ops::Deref for ExtKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExtKey> for ::std::string::String {
fn from(value: ExtKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExtKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ExtKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
/**
How the uploader will cut a blob for direct transfer, committed before any byte moves: one storage object, written in `partCount` parts of `partSize` bytes (the last may be shorter), each with its digest. Independent of the chunk manifest, because storage imposes part sizes a chunk document cannot carry; the chunk manifest still describes the blob and readers still check it.
Consistency rules JSON Schema cannot state: `partCount` MUST equal ceil(size / partSize) for the manifest's `size`, and `partDigests` MUST have exactly `partCount` items. Every digest MUST be sha2-256, because it is what the storage itself checks the part against.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "PartPlan",
/// "description": "How the uploader will cut a blob for direct transfer, committed before any byte moves: one storage object, written in `partCount` parts of `partSize` bytes (the last may be shorter), each with its digest. Independent of the chunk manifest, because storage imposes part sizes a chunk document cannot carry; the chunk manifest still describes the blob and readers still check it.\n\nConsistency rules JSON Schema cannot state: `partCount` MUST equal ceil(size / partSize) for the manifest's `size`, and `partDigests` MUST have exactly `partCount` items. Every digest MUST be sha2-256, because it is what the storage itself checks the part against.",
/// "type": "object",
/// "required": [
/// "partCount",
/// "partDigests",
/// "partSize"
/// ],
/// "properties": {
/// "partCount": {
/// "type": "integer",
/// "maximum": 1024.0,
/// "minimum": 1.0
/// },
/// "partDigests": {
/// "description": "The sha2-256 digest of each part's ciphertext, in order.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/DigestMultibase"
/// },
/// "maxItems": 1024,
/// "minItems": 1
/// },
/// "partSize": {
/// "description": "Bytes per part. One part of the whole blob's size is a single-object write.",
/// "type": "integer",
/// "maximum": 1073741824.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct PartPlan {
#[serde(rename = "partCount")]
pub part_count: ::std::num::NonZeroU64,
///The sha2-256 digest of each part's ciphertext, in order.
#[serde(rename = "partDigests")]
pub part_digests: ::std::vec::Vec<DigestMultibase>,
///Bytes per part. One part of the whole blob's size is a single-object write.
#[serde(rename = "partSize")]
pub part_size: ::std::num::NonZeroU64,
}
impl PartPlan {
pub fn builder() -> builder::PartPlan {
Default::default()
}
}
///Open an upload of one file's ciphertext into a data room, committing to its size and to the digest of every chunk before any byte moves, reserving room for it against every limit that applies, and saying how its bytes may move — straight to the room's storage, or through the host under a single-use upload key. The outer document members are owned by the framework — SPEC §6.3.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/rooms/blobs/upload/begin/0.2",
/// "title": "Payload",
/// "description": "Open an upload of one file's ciphertext into a data room, committing to its size and to the digest of every chunk before any byte moves, reserving room for it against every limit that applies, and saying how its bytes may move — straight to the room's storage, or through the host under a single-use upload key. The outer document members are owned by the framework — SPEC §6.3.",
/// "type": "object",
/// "required": [
/// "manifest",
/// "presentation",
/// "roomId"
/// ],
/// "properties": {
/// "directParts": {
/// "description": "How the producer will cut the blob for direct transfer. Present means the producer accepts direct transfer; absent means it accepts only proxied.",
/// "$ref": "#/definitions/PartPlan"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "manifest": {
/// "description": "The ciphertext manifest. Its digest is the BlobRef the blob will have once committed.",
/// "$ref": "#/definitions/BlobManifest"
/// },
/// "presentation": {
/// "description": "Must confer the `write` action at this room's scope. Its leaf's subject must be the party the host authenticated for this request.",
/// "$ref": "#/definitions/AuthorityPresentation"
/// },
/// "roomId": {
/// "description": "The room the file is for. The host verifies the presentation against credentials this identifier issued, never against a member list of its own.",
/// "type": "string",
/// "maxLength": 2048
/// },
/// "uploadKey": {
/// "description": "A did:key the producer generated for this upload alone, for a proxied transfer: it signs the upload's chunk, commit and abort documents, and nothing else, and the host never records it as the owner or the uploader. Ed25519 (`z6Mk…`) MUST be accepted. A producer that omits it accepts only direct transfer.",
/// "type": "string",
/// "maxLength": 256,
/// "pattern": "^did:key:z[1-9A-HJ-NP-Za-km-z]+$"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///How the producer will cut the blob for direct transfer. Present means the producer accepts direct transfer; absent means it accepts only proxied.
#[serde(
rename = "directParts",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub direct_parts: ::std::option::Option<PartPlan>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///The ciphertext manifest. Its digest is the BlobRef the blob will have once committed.
pub manifest: BlobManifest,
///Must confer the `write` action at this room's scope. Its leaf's subject must be the party the host authenticated for this request.
pub presentation: AuthorityPresentation,
///The room the file is for. The host verifies the presentation against credentials this identifier issued, never against a member list of its own.
#[serde(rename = "roomId")]
pub room_id: PayloadRoomId,
///A did:key the producer generated for this upload alone, for a proxied transfer: it signs the upload's chunk, commit and abort documents, and nothing else, and the host never records it as the owner or the uploader. Ed25519 (`z6Mk…`) MUST be accepted. A producer that omits it accepts only direct transfer.
#[serde(
rename = "uploadKey",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub upload_key: ::std::option::Option<PayloadUploadKey>,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///The room the file is for. The host verifies the presentation against credentials this identifier issued, never against a member list of its own.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The room the file is for. The host verifies the presentation against credentials this identifier issued, never against a member list of its own.",
/// "type": "string",
/// "maxLength": 2048
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadRoomId(::std::string::String);
impl ::std::ops::Deref for PayloadRoomId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadRoomId> for ::std::string::String {
fn from(value: PayloadRoomId) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadRoomId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadRoomId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadRoomId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadRoomId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadRoomId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A did:key the producer generated for this upload alone, for a proxied transfer: it signs the upload's chunk, commit and abort documents, and nothing else, and the host never records it as the owner or the uploader. Ed25519 (`z6Mk…`) MUST be accepted. A producer that omits it accepts only direct transfer.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "A did:key the producer generated for this upload alone, for a proxied transfer: it signs the upload's chunk, commit and abort documents, and nothing else, and the host never records it as the owner or the uploader. Ed25519 (`z6Mk…`) MUST be accepted. A producer that omits it accepts only direct transfer.",
/// "type": "string",
/// "maxLength": 256,
/// "pattern": "^did:key:z[1-9A-HJ-NP-Za-km-z]+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadUploadKey(::std::string::String);
impl ::std::ops::Deref for PayloadUploadKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadUploadKey> for ::std::string::String {
fn from(value: PayloadUploadKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadUploadKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 256usize {
return Err("longer than 256 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^did:key:z[1-9A-HJ-NP-Za-km-z]+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^did:key:z[1-9A-HJ-NP-Za-km-z]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadUploadKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadUploadKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadUploadKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadUploadKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Success response to rooms/blobs/upload/begin. Type https://trusttasks.org/spec/rooms/blobs/upload/begin/0.2#response.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "Success response to rooms/blobs/upload/begin. Type https://trusttasks.org/spec/rooms/blobs/upload/begin/0.2#response.",
/// "type": "object",
/// "required": [
/// "expiresAt",
/// "missing",
/// "mode",
/// "uploadId"
/// ],
/// "properties": {
/// "alreadyCommitted": {
/// "description": "True when the manifest's BlobRef is already committed in this room. `missing` is then empty, nothing was reserved, and commit returns the existing blob without charging it again. Absent or false on every other answer.",
/// "type": "boolean"
/// },
/// "expiresAt": {
/// "$ref": "#/definitions/ExpiresAt"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "missing": {
/// "description": "The indices the host does not yet hold, in ascending order: chunk indices in proxied mode, part indices in direct mode. Every index on a new upload; only what is left on a resumed one; none when `alreadyCommitted` is true. Empty means the upload can be committed.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/ChunkIndex"
/// },
/// "maxItems": 4096,
/// "uniqueItems": true
/// },
/// "mode": {
/// "description": "How this upload's bytes move: `direct` to the room's storage over `targets`, or `proxied` through the host as rooms/blobs/upload/chunk documents signed by `uploadKey`.",
/// "$ref": "#/definitions/TransferMode"
/// },
/// "targets": {
/// "description": "Direct mode only: a PUT target for each of some or all of the `missing` parts. A host MAY issue targets for only some of them; the producer begins again, with the identical manifest and plan, for the rest and for any whose target lapsed. Absent in proxied mode.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/TransferTarget"
/// },
/// "maxItems": 128
/// },
/// "uploadId": {
/// "$ref": "#/definitions/TransferId"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
///True when the manifest's BlobRef is already committed in this room. `missing` is then empty, nothing was reserved, and commit returns the existing blob without charging it again. Absent or false on every other answer.
#[serde(
rename = "alreadyCommitted",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub already_committed: ::std::option::Option<bool>,
#[serde(rename = "expiresAt")]
pub expires_at: ExpiresAt,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///The indices the host does not yet hold, in ascending order: chunk indices in proxied mode, part indices in direct mode. Every index on a new upload; only what is left on a resumed one; none when `alreadyCommitted` is true. Empty means the upload can be committed.
pub missing: Vec<ChunkIndex>,
///How this upload's bytes move: `direct` to the room's storage over `targets`, or `proxied` through the host as rooms/blobs/upload/chunk documents signed by `uploadKey`.
pub mode: TransferMode,
///Direct mode only: a PUT target for each of some or all of the `missing` parts. A host MAY issue targets for only some of them; the producer begins again, with the identical manifest and plan, for the rest and for any whose target lapsed. Absent in proxied mode.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub targets: ::std::vec::Vec<TransferTarget>,
#[serde(rename = "uploadId")]
pub upload_id: TransferId,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///Handle for one upload (begin → chunk → commit or abort) or one download (get → chunk). Host-generated and unguessable, which is what lets a reference to another party's transfer be answered as not-found without confirming that it exists. Bound at creation to the party that opened it: the subject the host authenticated for the opening request. Opaque: a producer quotes what it was given.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "TransferId",
/// "description": "Handle for one upload (begin → chunk → commit or abort) or one download (get → chunk). Host-generated and unguessable, which is what lets a reference to another party's transfer be answered as not-found without confirming that it exists. Bound at creation to the party that opened it: the subject the host authenticated for the opening request. Opaque: a producer quotes what it was given.",
/// "type": "string",
/// "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct TransferId(::std::string::String);
impl ::std::ops::Deref for TransferId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<TransferId> for ::std::string::String {
fn from(value: TransferId) -> Self {
value.0
}
}
impl ::std::str::FromStr for TransferId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new(
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
)
.unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for TransferId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TransferId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TransferId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for TransferId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///How a blob's bytes move. `proxied`: through the host, as chunk documents (rooms/blobs/upload/chunk, rooms/blobs/chunk). `direct`: straight between the member and the storage the room is assigned to, over time-limited URLs the host issues for one object and one method each; the host authorizes every object and verifies every upload, and never carries a byte. Which mode applies is the host's answer, from what the producer accepts, what the room's storage can do, and the room's tier.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "TransferMode",
/// "description": "How a blob's bytes move. `proxied`: through the host, as chunk documents (rooms/blobs/upload/chunk, rooms/blobs/chunk). `direct`: straight between the member and the storage the room is assigned to, over time-limited URLs the host issues for one object and one method each; the host authorizes every object and verifies every upload, and never carries a byte. Which mode applies is the host's answer, from what the producer accepts, what the room's storage can do, and the room's tier.",
/// "type": "string",
/// "enum": [
/// "direct",
/// "proxied"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum TransferMode {
#[serde(rename = "direct")]
Direct,
#[serde(rename = "proxied")]
Proxied,
}
impl ::std::fmt::Display for TransferMode {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Direct => f.write_str("direct"),
Self::Proxied => f.write_str("proxied"),
}
}
}
impl ::std::str::FromStr for TransferMode {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"direct" => Ok(Self::Direct),
"proxied" => Ok(Self::Proxied),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for TransferMode {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TransferMode {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TransferMode {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///One time-limited URL at the room's storage, issued by the host for one object and one method. A bearer credential for its lifetime: whoever holds it may perform exactly that request. A host MUST bind it to one object, one method and — for a write — the part's exact length and checksum, so that storage itself refuses any other bytes, and MUST give it the shortest practical lifetime, never more than 5 minutes. No party logs it.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "TransferTarget",
/// "description": "One time-limited URL at the room's storage, issued by the host for one object and one method. A bearer credential for its lifetime: whoever holds it may perform exactly that request. A host MUST bind it to one object, one method and — for a write — the part's exact length and checksum, so that storage itself refuses any other bytes, and MUST give it the shortest practical lifetime, never more than 5 minutes. No party logs it.",
/// "type": "object",
/// "required": [
/// "expiresAt",
/// "index",
/// "method",
/// "url"
/// ],
/// "properties": {
/// "expiresAt": {
/// "description": "When the storage stops honouring the URL. A request begun after it is refused by the storage.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "headers": {
/// "description": "Headers the request MUST carry exactly as given — for a write, the part's length and checksum, which the URL's signature covers. A client adds nothing the storage would treat as signed.",
/// "type": "object",
/// "maxProperties": 16,
/// "additionalProperties": {
/// "type": "string",
/// "maxLength": 1024
/// },
/// "propertyNames": {
/// "maxLength": 128
/// }
/// },
/// "index": {
/// "description": "The part this target writes, or 0 for a read of the whole object.",
/// "type": "integer",
/// "maximum": 1023.0,
/// "minimum": 0.0
/// },
/// "method": {
/// "type": "string",
/// "enum": [
/// "PUT",
/// "GET"
/// ]
/// },
/// "url": {
/// "description": "Where to send the request. HTTPS only.",
/// "type": "string",
/// "format": "uri",
/// "maxLength": 8192,
/// "pattern": "^https://"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct TransferTarget {
///When the storage stops honouring the URL. A request begun after it is refused by the storage.
#[serde(rename = "expiresAt")]
pub expires_at: ::chrono::DateTime<::chrono::offset::Utc>,
///Headers the request MUST carry exactly as given — for a write, the part's length and checksum, which the URL's signature covers. A client adds nothing the storage would treat as signed.
#[serde(
default,
skip_serializing_if = ":: std :: collections :: HashMap::is_empty"
)]
pub headers: ::std::collections::HashMap<TransferTargetHeadersKey, TransferTargetHeadersValue>,
///The part this target writes, or 0 for a read of the whole object.
pub index: i64,
pub method: TransferTargetMethod,
///Where to send the request. HTTPS only.
pub url: ::std::string::String,
}
impl TransferTarget {
pub fn builder() -> builder::TransferTarget {
Default::default()
}
}
///`TransferTargetHeadersKey`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct TransferTargetHeadersKey(::std::string::String);
impl ::std::ops::Deref for TransferTargetHeadersKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<TransferTargetHeadersKey> for ::std::string::String {
fn from(value: TransferTargetHeadersKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for TransferTargetHeadersKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for TransferTargetHeadersKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TransferTargetHeadersKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TransferTargetHeadersKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for TransferTargetHeadersKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`TransferTargetHeadersValue`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 1024
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct TransferTargetHeadersValue(::std::string::String);
impl ::std::ops::Deref for TransferTargetHeadersValue {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<TransferTargetHeadersValue> for ::std::string::String {
fn from(value: TransferTargetHeadersValue) -> Self {
value.0
}
}
impl ::std::str::FromStr for TransferTargetHeadersValue {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 1024usize {
return Err("longer than 1024 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for TransferTargetHeadersValue {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TransferTargetHeadersValue {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TransferTargetHeadersValue {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for TransferTargetHeadersValue {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`TransferTargetMethod`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "PUT",
/// "GET"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum TransferTargetMethod {
#[serde(rename = "PUT")]
Put,
#[serde(rename = "GET")]
Get,
}
impl ::std::fmt::Display for TransferTargetMethod {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Put => f.write_str("PUT"),
Self::Get => f.write_str("GET"),
}
}
}
impl ::std::str::FromStr for TransferTargetMethod {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"PUT" => Ok(Self::Put),
"GET" => Ok(Self::Get),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for TransferTargetMethod {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TransferTargetMethod {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TransferTargetMethod {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct AuthorityPresentation {
authority:
::std::result::Result<::std::vec::Vec<::std::string::String>, ::std::string::String>,
membership: ::std::result::Result<::std::string::String, ::std::string::String>,
subject_binding: ::std::result::Result<
::std::option::Option<::std::string::String>,
::std::string::String,
>,
}
impl ::std::default::Default for AuthorityPresentation {
fn default() -> Self {
Self {
authority: Err("no value supplied for authority".to_string()),
membership: Err("no value supplied for membership".to_string()),
subject_binding: Ok(Default::default()),
}
}
}
impl AuthorityPresentation {
pub fn authority<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.authority = value
.try_into()
.map_err(|e| format!("error converting supplied value for authority: {e}"));
self
}
pub fn membership<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::string::String>,
T::Error: ::std::fmt::Display,
{
self.membership = value
.try_into()
.map_err(|e| format!("error converting supplied value for membership: {e}"));
self
}
pub fn subject_binding<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::string::String>>,
T::Error: ::std::fmt::Display,
{
self.subject_binding = value
.try_into()
.map_err(|e| format!("error converting supplied value for subject_binding: {e}"));
self
}
}
impl ::std::convert::TryFrom<AuthorityPresentation> for super::AuthorityPresentation {
type Error = super::error::ConversionError;
fn try_from(
value: AuthorityPresentation,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
authority: value.authority?,
membership: value.membership?,
subject_binding: value.subject_binding?,
})
}
}
impl ::std::convert::From<super::AuthorityPresentation> for AuthorityPresentation {
fn from(value: super::AuthorityPresentation) -> Self {
Self {
authority: Ok(value.authority),
membership: Ok(value.membership),
subject_binding: Ok(value.subject_binding),
}
}
}
#[derive(Clone, Debug)]
pub struct BlobManifest {
chunks: ::std::result::Result<super::ChunkManifest, ::std::string::String>,
digest: ::std::result::Result<super::DigestMultibase, ::std::string::String>,
size: ::std::result::Result<super::ExpectedSizeBytes, ::std::string::String>,
}
impl ::std::default::Default for BlobManifest {
fn default() -> Self {
Self {
chunks: Err("no value supplied for chunks".to_string()),
digest: Err("no value supplied for digest".to_string()),
size: Err("no value supplied for size".to_string()),
}
}
}
impl BlobManifest {
pub fn chunks<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ChunkManifest>,
T::Error: ::std::fmt::Display,
{
self.chunks = value
.try_into()
.map_err(|e| format!("error converting supplied value for chunks: {e}"));
self
}
pub fn digest<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DigestMultibase>,
T::Error: ::std::fmt::Display,
{
self.digest = value
.try_into()
.map_err(|e| format!("error converting supplied value for digest: {e}"));
self
}
pub fn size<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ExpectedSizeBytes>,
T::Error: ::std::fmt::Display,
{
self.size = value
.try_into()
.map_err(|e| format!("error converting supplied value for size: {e}"));
self
}
}
impl ::std::convert::TryFrom<BlobManifest> for super::BlobManifest {
type Error = super::error::ConversionError;
fn try_from(
value: BlobManifest,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
chunks: value.chunks?,
digest: value.digest?,
size: value.size?,
})
}
}
impl ::std::convert::From<super::BlobManifest> for BlobManifest {
fn from(value: super::BlobManifest) -> Self {
Self {
chunks: Ok(value.chunks),
digest: Ok(value.digest),
size: Ok(value.size),
}
}
}
#[derive(Clone, Debug)]
pub struct ChunkManifest {
chunk_count: ::std::result::Result<super::ChunkCount, ::std::string::String>,
chunk_digests:
::std::result::Result<::std::vec::Vec<super::DigestMultibase>, ::std::string::String>,
chunk_size: ::std::result::Result<super::ChunkSize, ::std::string::String>,
}
impl ::std::default::Default for ChunkManifest {
fn default() -> Self {
Self {
chunk_count: Err("no value supplied for chunk_count".to_string()),
chunk_digests: Err("no value supplied for chunk_digests".to_string()),
chunk_size: Err("no value supplied for chunk_size".to_string()),
}
}
}
impl ChunkManifest {
pub fn chunk_count<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ChunkCount>,
T::Error: ::std::fmt::Display,
{
self.chunk_count = value
.try_into()
.map_err(|e| format!("error converting supplied value for chunk_count: {e}"));
self
}
pub fn chunk_digests<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::DigestMultibase>>,
T::Error: ::std::fmt::Display,
{
self.chunk_digests = value
.try_into()
.map_err(|e| format!("error converting supplied value for chunk_digests: {e}"));
self
}
pub fn chunk_size<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ChunkSize>,
T::Error: ::std::fmt::Display,
{
self.chunk_size = value
.try_into()
.map_err(|e| format!("error converting supplied value for chunk_size: {e}"));
self
}
}
impl ::std::convert::TryFrom<ChunkManifest> for super::ChunkManifest {
type Error = super::error::ConversionError;
fn try_from(
value: ChunkManifest,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
chunk_count: value.chunk_count?,
chunk_digests: value.chunk_digests?,
chunk_size: value.chunk_size?,
})
}
}
impl ::std::convert::From<super::ChunkManifest> for ChunkManifest {
fn from(value: super::ChunkManifest) -> Self {
Self {
chunk_count: Ok(value.chunk_count),
chunk_digests: Ok(value.chunk_digests),
chunk_size: Ok(value.chunk_size),
}
}
}
#[derive(Clone, Debug)]
pub struct PartPlan {
part_count: ::std::result::Result<::std::num::NonZeroU64, ::std::string::String>,
part_digests:
::std::result::Result<::std::vec::Vec<super::DigestMultibase>, ::std::string::String>,
part_size: ::std::result::Result<::std::num::NonZeroU64, ::std::string::String>,
}
impl ::std::default::Default for PartPlan {
fn default() -> Self {
Self {
part_count: Err("no value supplied for part_count".to_string()),
part_digests: Err("no value supplied for part_digests".to_string()),
part_size: Err("no value supplied for part_size".to_string()),
}
}
}
impl PartPlan {
pub fn part_count<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::num::NonZeroU64>,
T::Error: ::std::fmt::Display,
{
self.part_count = value
.try_into()
.map_err(|e| format!("error converting supplied value for part_count: {e}"));
self
}
pub fn part_digests<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::DigestMultibase>>,
T::Error: ::std::fmt::Display,
{
self.part_digests = value
.try_into()
.map_err(|e| format!("error converting supplied value for part_digests: {e}"));
self
}
pub fn part_size<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::num::NonZeroU64>,
T::Error: ::std::fmt::Display,
{
self.part_size = value
.try_into()
.map_err(|e| format!("error converting supplied value for part_size: {e}"));
self
}
}
impl ::std::convert::TryFrom<PartPlan> for super::PartPlan {
type Error = super::error::ConversionError;
fn try_from(value: PartPlan) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
part_count: value.part_count?,
part_digests: value.part_digests?,
part_size: value.part_size?,
})
}
}
impl ::std::convert::From<super::PartPlan> for PartPlan {
fn from(value: super::PartPlan) -> Self {
Self {
part_count: Ok(value.part_count),
part_digests: Ok(value.part_digests),
part_size: Ok(value.part_size),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
direct_parts:
::std::result::Result<::std::option::Option<super::PartPlan>, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
manifest: ::std::result::Result<super::BlobManifest, ::std::string::String>,
presentation: ::std::result::Result<super::AuthorityPresentation, ::std::string::String>,
room_id: ::std::result::Result<super::PayloadRoomId, ::std::string::String>,
upload_key: ::std::result::Result<
::std::option::Option<super::PayloadUploadKey>,
::std::string::String,
>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
direct_parts: Ok(Default::default()),
ext: Ok(Default::default()),
manifest: Err("no value supplied for manifest".to_string()),
presentation: Err("no value supplied for presentation".to_string()),
room_id: Err("no value supplied for room_id".to_string()),
upload_key: Ok(Default::default()),
}
}
}
impl Payload {
pub fn direct_parts<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PartPlan>>,
T::Error: ::std::fmt::Display,
{
self.direct_parts = value
.try_into()
.map_err(|e| format!("error converting supplied value for direct_parts: {e}"));
self
}
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn manifest<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::BlobManifest>,
T::Error: ::std::fmt::Display,
{
self.manifest = value
.try_into()
.map_err(|e| format!("error converting supplied value for manifest: {e}"));
self
}
pub fn presentation<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::AuthorityPresentation>,
T::Error: ::std::fmt::Display,
{
self.presentation = value
.try_into()
.map_err(|e| format!("error converting supplied value for presentation: {e}"));
self
}
pub fn room_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadRoomId>,
T::Error: ::std::fmt::Display,
{
self.room_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for room_id: {e}"));
self
}
pub fn upload_key<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PayloadUploadKey>>,
T::Error: ::std::fmt::Display,
{
self.upload_key = value
.try_into()
.map_err(|e| format!("error converting supplied value for upload_key: {e}"));
self
}
}
impl ::std::convert::TryFrom<Payload> for super::Payload {
type Error = super::error::ConversionError;
fn try_from(value: Payload) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
direct_parts: value.direct_parts?,
ext: value.ext?,
manifest: value.manifest?,
presentation: value.presentation?,
room_id: value.room_id?,
upload_key: value.upload_key?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
direct_parts: Ok(value.direct_parts),
ext: Ok(value.ext),
manifest: Ok(value.manifest),
presentation: Ok(value.presentation),
room_id: Ok(value.room_id),
upload_key: Ok(value.upload_key),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
already_committed:
::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
expires_at: ::std::result::Result<super::ExpiresAt, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
missing: ::std::result::Result<Vec<super::ChunkIndex>, ::std::string::String>,
mode: ::std::result::Result<super::TransferMode, ::std::string::String>,
targets:
::std::result::Result<::std::vec::Vec<super::TransferTarget>, ::std::string::String>,
upload_id: ::std::result::Result<super::TransferId, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
already_committed: Ok(Default::default()),
expires_at: Err("no value supplied for expires_at".to_string()),
ext: Ok(Default::default()),
missing: Err("no value supplied for missing".to_string()),
mode: Err("no value supplied for mode".to_string()),
targets: Ok(Default::default()),
upload_id: Err("no value supplied for upload_id".to_string()),
}
}
}
impl Response {
pub fn already_committed<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.already_committed = value
.try_into()
.map_err(|e| format!("error converting supplied value for already_committed: {e}"));
self
}
pub fn expires_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ExpiresAt>,
T::Error: ::std::fmt::Display,
{
self.expires_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for expires_at: {e}"));
self
}
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn missing<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<Vec<super::ChunkIndex>>,
T::Error: ::std::fmt::Display,
{
self.missing = value
.try_into()
.map_err(|e| format!("error converting supplied value for missing: {e}"));
self
}
pub fn mode<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::TransferMode>,
T::Error: ::std::fmt::Display,
{
self.mode = value
.try_into()
.map_err(|e| format!("error converting supplied value for mode: {e}"));
self
}
pub fn targets<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::TransferTarget>>,
T::Error: ::std::fmt::Display,
{
self.targets = value
.try_into()
.map_err(|e| format!("error converting supplied value for targets: {e}"));
self
}
pub fn upload_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::TransferId>,
T::Error: ::std::fmt::Display,
{
self.upload_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for upload_id: {e}"));
self
}
}
impl ::std::convert::TryFrom<Response> for super::Response {
type Error = super::error::ConversionError;
fn try_from(value: Response) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
already_committed: value.already_committed?,
expires_at: value.expires_at?,
ext: value.ext?,
missing: value.missing?,
mode: value.mode?,
targets: value.targets?,
upload_id: value.upload_id?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
already_committed: Ok(value.already_committed),
expires_at: Ok(value.expires_at),
ext: Ok(value.ext),
missing: Ok(value.missing),
mode: Ok(value.mode),
targets: Ok(value.targets),
upload_id: Ok(value.upload_id),
}
}
}
#[derive(Clone, Debug)]
pub struct TransferTarget {
expires_at:
::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
headers: ::std::result::Result<
::std::collections::HashMap<
super::TransferTargetHeadersKey,
super::TransferTargetHeadersValue,
>,
::std::string::String,
>,
index: ::std::result::Result<i64, ::std::string::String>,
method: ::std::result::Result<super::TransferTargetMethod, ::std::string::String>,
url: ::std::result::Result<::std::string::String, ::std::string::String>,
}
impl ::std::default::Default for TransferTarget {
fn default() -> Self {
Self {
expires_at: Err("no value supplied for expires_at".to_string()),
headers: Ok(Default::default()),
index: Err("no value supplied for index".to_string()),
method: Err("no value supplied for method".to_string()),
url: Err("no value supplied for url".to_string()),
}
}
}
impl TransferTarget {
pub fn expires_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.expires_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for expires_at: {e}"));
self
}
pub fn headers<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::collections::HashMap<
super::TransferTargetHeadersKey,
super::TransferTargetHeadersValue,
>,
>,
T::Error: ::std::fmt::Display,
{
self.headers = value
.try_into()
.map_err(|e| format!("error converting supplied value for headers: {e}"));
self
}
pub fn index<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<i64>,
T::Error: ::std::fmt::Display,
{
self.index = value
.try_into()
.map_err(|e| format!("error converting supplied value for index: {e}"));
self
}
pub fn method<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::TransferTargetMethod>,
T::Error: ::std::fmt::Display,
{
self.method = value
.try_into()
.map_err(|e| format!("error converting supplied value for method: {e}"));
self
}
pub fn url<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::string::String>,
T::Error: ::std::fmt::Display,
{
self.url = value
.try_into()
.map_err(|e| format!("error converting supplied value for url: {e}"));
self
}
}
impl ::std::convert::TryFrom<TransferTarget> for super::TransferTarget {
type Error = super::error::ConversionError;
fn try_from(
value: TransferTarget,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
expires_at: value.expires_at?,
headers: value.headers?,
index: value.index?,
method: value.method?,
url: value.url?,
})
}
}
impl ::std::convert::From<super::TransferTarget> for TransferTarget {
fn from(value: super::TransferTarget) -> Self {
Self {
expires_at: Ok(value.expires_at),
headers: Ok(value.headers),
index: Ok(value.index),
method: Ok(value.method),
url: Ok(value.url),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/rooms/blobs/upload/begin/0.2";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const MAX_DOCUMENT_BYTES: Option<usize> = Some(393216usize);
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"AuthorityPresentation\": {\n \"additionalProperties\": false,\n \"description\": \"What a party presents to act on a room. Carries the whole authority chain: a host MUST NOT dereference an authority credential's `parent` to fetch a link it was not given. Resolving over the network would make verification depend on availability, turn every identifier into a request the host can be induced to make against an address the holder chooses, and signal credential use to whoever hosts the identifier. A host MUST bind the presenter to the chain's leaf. A chain that verifies is evidence that authority was conferred on somebody; it is not evidence that the party presenting it is that somebody. The leaf's subject MUST equal the party the host authenticated for this request — an identity the transport established or a document `proof` proved, never one named in a payload. A host that omits this check authorizes every captured presentation, and the omission is silent, because the chain still verifies.\\n\\n**Every credential is its issuer's attestation, and only its issuer's.** For the membership credential and for every link of `authority`, a host MUST verify the credential's `proof` with a key that the credential's own `issuer` lists for `assertionMethod` in its DID document, and MUST refuse a credential whose proof names a verification method that is not its issuer's. A proof that verifies under some other party's key is that party's statement, not the issuer's, and confers nothing. The same binding holds for every other document a room signs, such as a `RevocationList`.\\n\\n**A chain that verifies may still be revoked.** A host that has accepted a `RevocationList` for the room (`rooms/revocations/put`) MUST, from the moment it accepted it, refuse every presentation in which the membership credential or any link of `authority` is revoked by that list, under every task and every version of every task it serves for the room. A chain from which any link is revoked is refused whole: a credential delegated from a revoked one confers nothing, however it was attenuated. The refusal is the same `permissionDenied` as any other failed presentation.\",\n \"properties\": {\n \"authority\": {\n \"description\": \"The authority chain, LEAF FIRST: the first element is the credential being relied on and the last MUST be one issued by the room itself. Every link the presenter relies on is present, because the host will not fetch one. Capped at 8: verification is linear in chain length and runs on every operation, so an unbounded chain is a denial-of-service surface against the host. The known uses need 2 to 3 — a person attenuating to an agent, and that agent to a sub-agent.\",\n \"items\": {\n \"type\": \"string\"\n },\n \"maxItems\": 8,\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"membership\": {\n \"description\": \"The presenter's membership credential for this room, or — on a `private` room — a zero-knowledge presentation of it. Serialized per the governing profile.\",\n \"type\": \"string\"\n },\n \"subjectBinding\": {\n \"description\": \"REQUIRED on a `private` room, where the subject identifier is withheld: a proof that the membership credential and the authority chain's leaf describe the SAME subject. Without it two parties pool credentials — one contributes membership, the other authority — and the combination verifies as a single party holding both. A host MUST refuse a private-room presentation that omits this.\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"membership\",\n \"authority\"\n ],\n \"title\": \"AuthorityPresentation\",\n \"type\": \"object\"\n },\n \"BlobManifest\": {\n \"additionalProperties\": false,\n \"description\": \"The ciphertext manifest of one blob, committed by the uploader before any byte moves. Its digest is the BlobRef. Every chunk is checked against `chunks.chunkDigests` on arrival and the reassembled blob against `digest`, so a host can neither accept nor serve bytes the uploader did not commit to. The manifest also appears, by its BlobRef, inside the sealed FileManifest the author signs; a reader who opens the record therefore knows which ciphertext its author meant, and a host cannot substitute a different blob under the same record.\\n\\nConsistency rules JSON Schema cannot state: `chunks.chunkCount` MUST equal ceil(size / chunks.chunkSize), and `chunks.chunkDigests` MUST have exactly `chunkCount` items. A party receiving a manifest violating either MUST refuse it.\\n\\nEach transfer chunk is exactly one sealed STREAM segment (see FileManifest): every chunk but the last is `chunkSize` bytes, and `chunkSize` is the plaintext segment size plus the 16-byte AEAD tag. The 262144-byte ceiling of ChunkSize therefore puts the largest plaintext segment at 262128 bytes, and the 4096-chunk ceiling of ChunkCount puts the largest blob at 1 GiB of ciphertext.\",\n \"properties\": {\n \"chunks\": {\n \"$ref\": \"#/$defs/ChunkManifest\"\n },\n \"digest\": {\n \"$ref\": \"#/$defs/DigestMultibase\",\n \"description\": \"Digest of the whole ciphertext, over its bytes. Checked after reassembly, so a correct set of chunks in the wrong order is still caught.\"\n },\n \"size\": {\n \"$ref\": \"#/$defs/ExpectedSizeBytes\",\n \"description\": \"Ciphertext bytes, padding included. What the host stores, and what every limit and usage figure is stated in.\"\n }\n },\n \"required\": [\n \"size\",\n \"chunks\",\n \"digest\"\n ],\n \"title\": \"BlobManifest\",\n \"type\": \"object\"\n },\n \"ChunkCount\": {\n \"description\": \"Number of chunks in the bundle, equal to ceil(expectedSizeBytes / chunkSize). Bounded at 4096 so that the manifest itself — one digest per chunk — fits in the single document that carries it under the same message-size reasoning as a chunk.\",\n \"maximum\": 4096,\n \"minimum\": 1,\n \"title\": \"ChunkCount\",\n \"type\": \"integer\"\n },\n \"ChunkIndex\": {\n \"description\": \"Zero-based position of a chunk within its bundle. Valid values are 0 to chunkCount − 1; an index outside that range is refused with chunkOutOfRange rather than validated here, because the bound depends on the bundle.\",\n \"maximum\": 4095,\n \"minimum\": 0,\n \"title\": \"ChunkIndex\",\n \"type\": \"integer\"\n },\n \"ChunkManifest\": {\n \"additionalProperties\": false,\n \"description\": \"The terms of a `chunkedTrustTask` transfer, committed before any chunk moves. On export the recipient states them in the descriptor; on import the producer pre-commits them in the request and the recipient echoes them. Either way the manifest arrives in a document whose proof is REQUIRED, so the per-chunk digests are authenticated by the party that computed them and each chunk can be verified — and a single bad chunk re-fetched or refused — on arrival rather than only after reassembly.\\n\\nConsistency rules JSON Schema cannot state: `chunkCount` MUST equal ceil(expectedSizeBytes / chunkSize) for the bundle the manifest describes, and `chunkDigests` MUST have exactly `chunkCount` items. A party receiving a manifest violating either MUST refuse it.\",\n \"properties\": {\n \"chunkCount\": {\n \"$ref\": \"#/$defs/ChunkCount\"\n },\n \"chunkDigests\": {\n \"description\": \"Digest of each chunk's raw bytes (not of its base64url encoding), in index order. Compared as decoded multihash bytes, never as encoded strings. sha2-256 is RECOMMENDED and MUST be implemented by every party; a party that does not implement the hash a digest names MUST treat the manifest as unverifiable rather than skip the check.\",\n \"items\": {\n \"$ref\": \"#/$defs/DigestMultibase\"\n },\n \"maxItems\": 4096,\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"chunkSize\": {\n \"$ref\": \"#/$defs/ChunkSize\"\n }\n },\n \"required\": [\n \"chunkSize\",\n \"chunkCount\",\n \"chunkDigests\"\n ],\n \"title\": \"ChunkManifest\",\n \"type\": \"object\"\n },\n \"ChunkSize\": {\n \"description\": \"Size in bytes of every chunk except the last, which carries the remainder and is between 1 and this value inclusive. The ceiling of 262144 (256 KiB) is normative and is derived in `vta/backup/initiate-export/1.1` under Chunked transfer: it is the largest power of two whose `get-chunk` or `put-chunk` document still fits a 1 MiB mediator message after base64url encoding of `data`, DIDComm authcrypt encoding, and two nested forward wrappers. The floor of 16384 keeps a 1 GiB bundle within `ChunkCount`'s ceiling at sizes a constrained transport can still choose.\",\n \"maximum\": 262144,\n \"minimum\": 16384,\n \"title\": \"ChunkSize\",\n \"type\": \"integer\"\n },\n \"DigestMultibase\": {\n \"description\": \"A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\\n\\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\\n\\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\\n\\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \\\"interoperability is not guaranteed between implementations using such values\\\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.\",\n \"examples\": [\n \"zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\"\n ],\n \"minLength\": 16,\n \"pattern\": \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\n \"title\": \"DigestMultibase\",\n \"type\": \"string\"\n },\n \"ExpectedSizeBytes\": {\n \"description\": \"Total byte count of the bundle. A zero-length bundle is not a degenerate success — nothing was serialized — so the floor is 1.\",\n \"minimum\": 1,\n \"title\": \"ExpectedSizeBytes\",\n \"type\": \"integer\"\n },\n \"ExpiresAt\": {\n \"description\": \"After which the bundle is collected: staged bytes discarded, tokens and chunk requests refused. Short by design. For a chunked transfer a recipient may move it later as chunks are exchanged, never past its own ceiling — each chunk response reports the current value.\",\n \"format\": \"date-time\",\n \"title\": \"ExpiresAt\",\n \"type\": \"string\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"PartPlan\": {\n \"additionalProperties\": false,\n \"description\": \"How the uploader will cut a blob for direct transfer, committed before any byte moves: one storage object, written in `partCount` parts of `partSize` bytes (the last may be shorter), each with its digest. Independent of the chunk manifest, because storage imposes part sizes a chunk document cannot carry; the chunk manifest still describes the blob and readers still check it.\\n\\nConsistency rules JSON Schema cannot state: `partCount` MUST equal ceil(size / partSize) for the manifest's `size`, and `partDigests` MUST have exactly `partCount` items. Every digest MUST be sha2-256, because it is what the storage itself checks the part against.\",\n \"properties\": {\n \"partCount\": {\n \"maximum\": 1024,\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"partDigests\": {\n \"description\": \"The sha2-256 digest of each part's ciphertext, in order.\",\n \"items\": {\n \"$ref\": \"#/$defs/DigestMultibase\"\n },\n \"maxItems\": 1024,\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"partSize\": {\n \"description\": \"Bytes per part. One part of the whole blob's size is a single-object write.\",\n \"maximum\": 1073741824,\n \"minimum\": 1,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"partSize\",\n \"partCount\",\n \"partDigests\"\n ],\n \"title\": \"PartPlan\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Success response to rooms/blobs/upload/begin. Type https://trusttasks.org/spec/rooms/blobs/upload/begin/0.2#response.\",\n \"properties\": {\n \"alreadyCommitted\": {\n \"description\": \"True when the manifest's BlobRef is already committed in this room. `missing` is then empty, nothing was reserved, and commit returns the existing blob without charging it again. Absent or false on every other answer.\",\n \"type\": \"boolean\"\n },\n \"expiresAt\": {\n \"$ref\": \"#/$defs/ExpiresAt\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"missing\": {\n \"description\": \"The indices the host does not yet hold, in ascending order: chunk indices in proxied mode, part indices in direct mode. Every index on a new upload; only what is left on a resumed one; none when `alreadyCommitted` is true. Empty means the upload can be committed.\",\n \"items\": {\n \"$ref\": \"#/$defs/ChunkIndex\"\n },\n \"maxItems\": 4096,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"mode\": {\n \"$ref\": \"#/$defs/TransferMode\",\n \"description\": \"How this upload's bytes move: `direct` to the room's storage over `targets`, or `proxied` through the host as rooms/blobs/upload/chunk documents signed by `uploadKey`.\"\n },\n \"targets\": {\n \"description\": \"Direct mode only: a PUT target for each of some or all of the `missing` parts. A host MAY issue targets for only some of them; the producer begins again, with the identical manifest and plan, for the rest and for any whose target lapsed. Absent in proxied mode.\",\n \"items\": {\n \"$ref\": \"#/$defs/TransferTarget\"\n },\n \"maxItems\": 128,\n \"type\": \"array\"\n },\n \"uploadId\": {\n \"$ref\": \"#/$defs/TransferId\"\n }\n },\n \"required\": [\n \"uploadId\",\n \"mode\",\n \"missing\",\n \"expiresAt\"\n ],\n \"title\": \"Rooms Blobs — Upload — Begin — response payload\",\n \"type\": \"object\"\n },\n \"TransferId\": {\n \"description\": \"Handle for one upload (begin → chunk → commit or abort) or one download (get → chunk). Host-generated and unguessable, which is what lets a reference to another party's transfer be answered as not-found without confirming that it exists. Bound at creation to the party that opened it: the subject the host authenticated for the opening request. Opaque: a producer quotes what it was given.\",\n \"pattern\": \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\",\n \"title\": \"TransferId\",\n \"type\": \"string\"\n },\n \"TransferMode\": {\n \"description\": \"How a blob's bytes move. `proxied`: through the host, as chunk documents (rooms/blobs/upload/chunk, rooms/blobs/chunk). `direct`: straight between the member and the storage the room is assigned to, over time-limited URLs the host issues for one object and one method each; the host authorizes every object and verifies every upload, and never carries a byte. Which mode applies is the host's answer, from what the producer accepts, what the room's storage can do, and the room's tier.\",\n \"enum\": [\n \"direct\",\n \"proxied\"\n ],\n \"title\": \"TransferMode\",\n \"type\": \"string\"\n },\n \"TransferTarget\": {\n \"additionalProperties\": false,\n \"description\": \"One time-limited URL at the room's storage, issued by the host for one object and one method. A bearer credential for its lifetime: whoever holds it may perform exactly that request. A host MUST bind it to one object, one method and — for a write — the part's exact length and checksum, so that storage itself refuses any other bytes, and MUST give it the shortest practical lifetime, never more than 5 minutes. No party logs it.\",\n \"properties\": {\n \"expiresAt\": {\n \"description\": \"When the storage stops honouring the URL. A request begun after it is refused by the storage.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"headers\": {\n \"additionalProperties\": {\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"description\": \"Headers the request MUST carry exactly as given — for a write, the part's length and checksum, which the URL's signature covers. A client adds nothing the storage would treat as signed.\",\n \"maxProperties\": 16,\n \"propertyNames\": {\n \"maxLength\": 128\n },\n \"type\": \"object\"\n },\n \"index\": {\n \"description\": \"The part this target writes, or 0 for a read of the whole object.\",\n \"maximum\": 1023,\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"method\": {\n \"enum\": [\n \"PUT\",\n \"GET\"\n ],\n \"type\": \"string\"\n },\n \"url\": {\n \"description\": \"Where to send the request. HTTPS only.\",\n \"format\": \"uri\",\n \"maxLength\": 8192,\n \"pattern\": \"^https://\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"index\",\n \"method\",\n \"url\",\n \"expiresAt\"\n ],\n \"title\": \"TransferTarget\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/rooms/blobs/upload/begin/0.2\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"Open an upload of one file's ciphertext into a data room, committing to its size and to the digest of every chunk before any byte moves, reserving room for it against every limit that applies, and saying how its bytes may move — straight to the room's storage, or through the host under a single-use upload key. The outer document members are owned by the framework — SPEC §6.3.\",\n \"properties\": {\n \"directParts\": {\n \"$ref\": \"#/$defs/PartPlan\",\n \"description\": \"How the producer will cut the blob for direct transfer. Present means the producer accepts direct transfer; absent means it accepts only proxied.\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"manifest\": {\n \"$ref\": \"#/$defs/BlobManifest\",\n \"description\": \"The ciphertext manifest. Its digest is the BlobRef the blob will have once committed.\"\n },\n \"presentation\": {\n \"$ref\": \"#/$defs/AuthorityPresentation\",\n \"description\": \"Must confer the `write` action at this room's scope. Its leaf's subject must be the party the host authenticated for this request.\"\n },\n \"roomId\": {\n \"description\": \"The room the file is for. The host verifies the presentation against credentials this identifier issued, never against a member list of its own.\",\n \"maxLength\": 2048,\n \"type\": \"string\"\n },\n \"uploadKey\": {\n \"description\": \"A did:key the producer generated for this upload alone, for a proxied transfer: it signs the upload's chunk, commit and abort documents, and nothing else, and the host never records it as the owner or the uploader. Ed25519 (`z6Mk…`) MUST be accepted. A producer that omits it accepts only direct transfer.\",\n \"maxLength\": 256,\n \"pattern\": \"^did:key:z[1-9A-HJ-NP-Za-km-z]+$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"roomId\",\n \"presentation\",\n \"manifest\"\n ],\n \"title\": \"Rooms Blobs — Upload — Begin — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/rooms/blobs/upload/begin/0.2#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const MAX_DOCUMENT_BYTES: Option<usize> = Some(327680usize);
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"AuthorityPresentation\": {\n \"additionalProperties\": false,\n \"description\": \"What a party presents to act on a room. Carries the whole authority chain: a host MUST NOT dereference an authority credential's `parent` to fetch a link it was not given. Resolving over the network would make verification depend on availability, turn every identifier into a request the host can be induced to make against an address the holder chooses, and signal credential use to whoever hosts the identifier. A host MUST bind the presenter to the chain's leaf. A chain that verifies is evidence that authority was conferred on somebody; it is not evidence that the party presenting it is that somebody. The leaf's subject MUST equal the party the host authenticated for this request — an identity the transport established or a document `proof` proved, never one named in a payload. A host that omits this check authorizes every captured presentation, and the omission is silent, because the chain still verifies.\\n\\n**Every credential is its issuer's attestation, and only its issuer's.** For the membership credential and for every link of `authority`, a host MUST verify the credential's `proof` with a key that the credential's own `issuer` lists for `assertionMethod` in its DID document, and MUST refuse a credential whose proof names a verification method that is not its issuer's. A proof that verifies under some other party's key is that party's statement, not the issuer's, and confers nothing. The same binding holds for every other document a room signs, such as a `RevocationList`.\\n\\n**A chain that verifies may still be revoked.** A host that has accepted a `RevocationList` for the room (`rooms/revocations/put`) MUST, from the moment it accepted it, refuse every presentation in which the membership credential or any link of `authority` is revoked by that list, under every task and every version of every task it serves for the room. A chain from which any link is revoked is refused whole: a credential delegated from a revoked one confers nothing, however it was attenuated. The refusal is the same `permissionDenied` as any other failed presentation.\",\n \"properties\": {\n \"authority\": {\n \"description\": \"The authority chain, LEAF FIRST: the first element is the credential being relied on and the last MUST be one issued by the room itself. Every link the presenter relies on is present, because the host will not fetch one. Capped at 8: verification is linear in chain length and runs on every operation, so an unbounded chain is a denial-of-service surface against the host. The known uses need 2 to 3 — a person attenuating to an agent, and that agent to a sub-agent.\",\n \"items\": {\n \"type\": \"string\"\n },\n \"maxItems\": 8,\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"membership\": {\n \"description\": \"The presenter's membership credential for this room, or — on a `private` room — a zero-knowledge presentation of it. Serialized per the governing profile.\",\n \"type\": \"string\"\n },\n \"subjectBinding\": {\n \"description\": \"REQUIRED on a `private` room, where the subject identifier is withheld: a proof that the membership credential and the authority chain's leaf describe the SAME subject. Without it two parties pool credentials — one contributes membership, the other authority — and the combination verifies as a single party holding both. A host MUST refuse a private-room presentation that omits this.\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"membership\",\n \"authority\"\n ],\n \"title\": \"AuthorityPresentation\",\n \"type\": \"object\"\n },\n \"BlobManifest\": {\n \"additionalProperties\": false,\n \"description\": \"The ciphertext manifest of one blob, committed by the uploader before any byte moves. Its digest is the BlobRef. Every chunk is checked against `chunks.chunkDigests` on arrival and the reassembled blob against `digest`, so a host can neither accept nor serve bytes the uploader did not commit to. The manifest also appears, by its BlobRef, inside the sealed FileManifest the author signs; a reader who opens the record therefore knows which ciphertext its author meant, and a host cannot substitute a different blob under the same record.\\n\\nConsistency rules JSON Schema cannot state: `chunks.chunkCount` MUST equal ceil(size / chunks.chunkSize), and `chunks.chunkDigests` MUST have exactly `chunkCount` items. A party receiving a manifest violating either MUST refuse it.\\n\\nEach transfer chunk is exactly one sealed STREAM segment (see FileManifest): every chunk but the last is `chunkSize` bytes, and `chunkSize` is the plaintext segment size plus the 16-byte AEAD tag. The 262144-byte ceiling of ChunkSize therefore puts the largest plaintext segment at 262128 bytes, and the 4096-chunk ceiling of ChunkCount puts the largest blob at 1 GiB of ciphertext.\",\n \"properties\": {\n \"chunks\": {\n \"$ref\": \"#/$defs/ChunkManifest\"\n },\n \"digest\": {\n \"$ref\": \"#/$defs/DigestMultibase\",\n \"description\": \"Digest of the whole ciphertext, over its bytes. Checked after reassembly, so a correct set of chunks in the wrong order is still caught.\"\n },\n \"size\": {\n \"$ref\": \"#/$defs/ExpectedSizeBytes\",\n \"description\": \"Ciphertext bytes, padding included. What the host stores, and what every limit and usage figure is stated in.\"\n }\n },\n \"required\": [\n \"size\",\n \"chunks\",\n \"digest\"\n ],\n \"title\": \"BlobManifest\",\n \"type\": \"object\"\n },\n \"ChunkCount\": {\n \"description\": \"Number of chunks in the bundle, equal to ceil(expectedSizeBytes / chunkSize). Bounded at 4096 so that the manifest itself — one digest per chunk — fits in the single document that carries it under the same message-size reasoning as a chunk.\",\n \"maximum\": 4096,\n \"minimum\": 1,\n \"title\": \"ChunkCount\",\n \"type\": \"integer\"\n },\n \"ChunkIndex\": {\n \"description\": \"Zero-based position of a chunk within its bundle. Valid values are 0 to chunkCount − 1; an index outside that range is refused with chunkOutOfRange rather than validated here, because the bound depends on the bundle.\",\n \"maximum\": 4095,\n \"minimum\": 0,\n \"title\": \"ChunkIndex\",\n \"type\": \"integer\"\n },\n \"ChunkManifest\": {\n \"additionalProperties\": false,\n \"description\": \"The terms of a `chunkedTrustTask` transfer, committed before any chunk moves. On export the recipient states them in the descriptor; on import the producer pre-commits them in the request and the recipient echoes them. Either way the manifest arrives in a document whose proof is REQUIRED, so the per-chunk digests are authenticated by the party that computed them and each chunk can be verified — and a single bad chunk re-fetched or refused — on arrival rather than only after reassembly.\\n\\nConsistency rules JSON Schema cannot state: `chunkCount` MUST equal ceil(expectedSizeBytes / chunkSize) for the bundle the manifest describes, and `chunkDigests` MUST have exactly `chunkCount` items. A party receiving a manifest violating either MUST refuse it.\",\n \"properties\": {\n \"chunkCount\": {\n \"$ref\": \"#/$defs/ChunkCount\"\n },\n \"chunkDigests\": {\n \"description\": \"Digest of each chunk's raw bytes (not of its base64url encoding), in index order. Compared as decoded multihash bytes, never as encoded strings. sha2-256 is RECOMMENDED and MUST be implemented by every party; a party that does not implement the hash a digest names MUST treat the manifest as unverifiable rather than skip the check.\",\n \"items\": {\n \"$ref\": \"#/$defs/DigestMultibase\"\n },\n \"maxItems\": 4096,\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"chunkSize\": {\n \"$ref\": \"#/$defs/ChunkSize\"\n }\n },\n \"required\": [\n \"chunkSize\",\n \"chunkCount\",\n \"chunkDigests\"\n ],\n \"title\": \"ChunkManifest\",\n \"type\": \"object\"\n },\n \"ChunkSize\": {\n \"description\": \"Size in bytes of every chunk except the last, which carries the remainder and is between 1 and this value inclusive. The ceiling of 262144 (256 KiB) is normative and is derived in `vta/backup/initiate-export/1.1` under Chunked transfer: it is the largest power of two whose `get-chunk` or `put-chunk` document still fits a 1 MiB mediator message after base64url encoding of `data`, DIDComm authcrypt encoding, and two nested forward wrappers. The floor of 16384 keeps a 1 GiB bundle within `ChunkCount`'s ceiling at sizes a constrained transport can still choose.\",\n \"maximum\": 262144,\n \"minimum\": 16384,\n \"title\": \"ChunkSize\",\n \"type\": \"integer\"\n },\n \"DigestMultibase\": {\n \"description\": \"A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\\n\\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\\n\\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\\n\\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \\\"interoperability is not guaranteed between implementations using such values\\\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.\",\n \"examples\": [\n \"zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR\"\n ],\n \"minLength\": 16,\n \"pattern\": \"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\n \"title\": \"DigestMultibase\",\n \"type\": \"string\"\n },\n \"ExpectedSizeBytes\": {\n \"description\": \"Total byte count of the bundle. A zero-length bundle is not a degenerate success — nothing was serialized — so the floor is 1.\",\n \"minimum\": 1,\n \"title\": \"ExpectedSizeBytes\",\n \"type\": \"integer\"\n },\n \"ExpiresAt\": {\n \"description\": \"After which the bundle is collected: staged bytes discarded, tokens and chunk requests refused. Short by design. For a chunked transfer a recipient may move it later as chunks are exchanged, never past its own ceiling — each chunk response reports the current value.\",\n \"format\": \"date-time\",\n \"title\": \"ExpiresAt\",\n \"type\": \"string\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"PartPlan\": {\n \"additionalProperties\": false,\n \"description\": \"How the uploader will cut a blob for direct transfer, committed before any byte moves: one storage object, written in `partCount` parts of `partSize` bytes (the last may be shorter), each with its digest. Independent of the chunk manifest, because storage imposes part sizes a chunk document cannot carry; the chunk manifest still describes the blob and readers still check it.\\n\\nConsistency rules JSON Schema cannot state: `partCount` MUST equal ceil(size / partSize) for the manifest's `size`, and `partDigests` MUST have exactly `partCount` items. Every digest MUST be sha2-256, because it is what the storage itself checks the part against.\",\n \"properties\": {\n \"partCount\": {\n \"maximum\": 1024,\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"partDigests\": {\n \"description\": \"The sha2-256 digest of each part's ciphertext, in order.\",\n \"items\": {\n \"$ref\": \"#/$defs/DigestMultibase\"\n },\n \"maxItems\": 1024,\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"partSize\": {\n \"description\": \"Bytes per part. One part of the whole blob's size is a single-object write.\",\n \"maximum\": 1073741824,\n \"minimum\": 1,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"partSize\",\n \"partCount\",\n \"partDigests\"\n ],\n \"title\": \"PartPlan\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Success response to rooms/blobs/upload/begin. Type https://trusttasks.org/spec/rooms/blobs/upload/begin/0.2#response.\",\n \"properties\": {\n \"alreadyCommitted\": {\n \"description\": \"True when the manifest's BlobRef is already committed in this room. `missing` is then empty, nothing was reserved, and commit returns the existing blob without charging it again. Absent or false on every other answer.\",\n \"type\": \"boolean\"\n },\n \"expiresAt\": {\n \"$ref\": \"#/$defs/ExpiresAt\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"missing\": {\n \"description\": \"The indices the host does not yet hold, in ascending order: chunk indices in proxied mode, part indices in direct mode. Every index on a new upload; only what is left on a resumed one; none when `alreadyCommitted` is true. Empty means the upload can be committed.\",\n \"items\": {\n \"$ref\": \"#/$defs/ChunkIndex\"\n },\n \"maxItems\": 4096,\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"mode\": {\n \"$ref\": \"#/$defs/TransferMode\",\n \"description\": \"How this upload's bytes move: `direct` to the room's storage over `targets`, or `proxied` through the host as rooms/blobs/upload/chunk documents signed by `uploadKey`.\"\n },\n \"targets\": {\n \"description\": \"Direct mode only: a PUT target for each of some or all of the `missing` parts. A host MAY issue targets for only some of them; the producer begins again, with the identical manifest and plan, for the rest and for any whose target lapsed. Absent in proxied mode.\",\n \"items\": {\n \"$ref\": \"#/$defs/TransferTarget\"\n },\n \"maxItems\": 128,\n \"type\": \"array\"\n },\n \"uploadId\": {\n \"$ref\": \"#/$defs/TransferId\"\n }\n },\n \"required\": [\n \"uploadId\",\n \"mode\",\n \"missing\",\n \"expiresAt\"\n ],\n \"title\": \"Rooms Blobs — Upload — Begin — response payload\",\n \"type\": \"object\"\n },\n \"TransferId\": {\n \"description\": \"Handle for one upload (begin → chunk → commit or abort) or one download (get → chunk). Host-generated and unguessable, which is what lets a reference to another party's transfer be answered as not-found without confirming that it exists. Bound at creation to the party that opened it: the subject the host authenticated for the opening request. Opaque: a producer quotes what it was given.\",\n \"pattern\": \"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$\",\n \"title\": \"TransferId\",\n \"type\": \"string\"\n },\n \"TransferMode\": {\n \"description\": \"How a blob's bytes move. `proxied`: through the host, as chunk documents (rooms/blobs/upload/chunk, rooms/blobs/chunk). `direct`: straight between the member and the storage the room is assigned to, over time-limited URLs the host issues for one object and one method each; the host authorizes every object and verifies every upload, and never carries a byte. Which mode applies is the host's answer, from what the producer accepts, what the room's storage can do, and the room's tier.\",\n \"enum\": [\n \"direct\",\n \"proxied\"\n ],\n \"title\": \"TransferMode\",\n \"type\": \"string\"\n },\n \"TransferTarget\": {\n \"additionalProperties\": false,\n \"description\": \"One time-limited URL at the room's storage, issued by the host for one object and one method. A bearer credential for its lifetime: whoever holds it may perform exactly that request. A host MUST bind it to one object, one method and — for a write — the part's exact length and checksum, so that storage itself refuses any other bytes, and MUST give it the shortest practical lifetime, never more than 5 minutes. No party logs it.\",\n \"properties\": {\n \"expiresAt\": {\n \"description\": \"When the storage stops honouring the URL. A request begun after it is refused by the storage.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"headers\": {\n \"additionalProperties\": {\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"description\": \"Headers the request MUST carry exactly as given — for a write, the part's length and checksum, which the URL's signature covers. A client adds nothing the storage would treat as signed.\",\n \"maxProperties\": 16,\n \"propertyNames\": {\n \"maxLength\": 128\n },\n \"type\": \"object\"\n },\n \"index\": {\n \"description\": \"The part this target writes, or 0 for a read of the whole object.\",\n \"maximum\": 1023,\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"method\": {\n \"enum\": [\n \"PUT\",\n \"GET\"\n ],\n \"type\": \"string\"\n },\n \"url\": {\n \"description\": \"Where to send the request. HTTPS only.\",\n \"format\": \"uri\",\n \"maxLength\": 8192,\n \"pattern\": \"^https://\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"index\",\n \"method\",\n \"url\",\n \"expiresAt\"\n ],\n \"title\": \"TransferTarget\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The extended error codes this specification declares (SPEC §7.3 item 9,
/// §8.5), in declaration order. Empty when it declares none.
pub const ERROR_CODES: &[crate::DeclaredErrorCode] = &[
error_codes::FILES_DISABLED,
error_codes::INVALID_MANIFEST,
error_codes::LIMIT_EXCEEDED,
error_codes::NO_TRANSFER_MODE,
error_codes::INVALID_PARTS,
error_codes::UPLOAD_KEY_IN_USE,
error_codes::TOO_MANY_UPLOADS,
];
/// One constant per extended error code this specification declares
/// (SPEC §7.3 item 9), named for its local part.
///
/// Emit these rather than a string literal: the code is read from the
/// specification, so it cannot name a code the specification never
/// declared.
pub mod error_codes {
/// `rooms/blobs/upload/begin:filesDisabled`
///
/// The host stores no files for this room. Records without files are unaffected.
///
/// Declared `retryable: false`.
pub const FILES_DISABLED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/blobs/upload/begin:filesDisabled",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/blobs/upload/begin:invalidManifest`
///
/// `chunks.chunkCount` is not ceil(size / chunks.chunkSize), or `chunks.chunkDigests` does not have exactly `chunkCount` items, or a digest names a hash the host does not implement.
///
/// Declared `retryable: false`.
pub const INVALID_MANIFEST: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/blobs/upload/begin:invalidManifest",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/blobs/upload/begin:limitExceeded`
///
/// The upload would not fit one of the limits that apply. `details` names the scope whose limit refuses it (for `maxFileBytes`, the scope the smallest applicable limit comes from), the measure, the limit, current use with reservations, and what this upload would add.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const LIMIT_EXCEEDED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/blobs/upload/begin:limitExceeded",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"limit\":{\"minimum\":0,\"type\":\"integer\"},\"measure\":{\"enum\":[\"maxFiles\",\"maxFileBytes\",\"maxBytes\"],\"type\":\"string\"},\"requested\":{\"minimum\":0,\"type\":\"integer\"},\"scope\":{\"enum\":[\"member\",\"room\",\"storage\",\"host\"],\"type\":\"string\"},\"used\":{\"minimum\":0,\"type\":\"integer\"}},\"required\":[\"scope\",\"measure\",\"limit\",\"used\",\"requested\"],\"type\":\"object\"}",
),
};
/// `rooms/blobs/upload/begin:noTransferMode`
///
/// No transfer mode is open to both sides: the producer offered no `uploadKey` (so accepts no proxied transfer) and the host will not transfer this upload directly — the room's storage cannot issue part URLs, the room's tier or the host's policy rules it out, or the part plan does not suit the storage. `details.modes` names the modes the host would use.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const NO_TRANSFER_MODE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/blobs/upload/begin:noTransferMode",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"modes\":{\"items\":{\"enum\":[\"direct\",\"proxied\"],\"type\":\"string\"},\"type\":\"array\",\"uniqueItems\":true}},\"required\":[\"modes\"],\"type\":\"object\"}",
),
};
/// `rooms/blobs/upload/begin:invalidParts`
///
/// `directParts` is inconsistent with the manifest: `partCount` is not ceil(size / partSize), `partDigests` does not have `partCount` items, or a digest is not sha2-256.
///
/// Declared `retryable: false`.
pub const INVALID_PARTS: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/blobs/upload/begin:invalidParts",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/blobs/upload/begin:uploadKeyInUse`
///
/// `uploadKey` is already bound to another open upload. An upload key is minted for one upload and dies with it; generate a fresh one.
///
/// Declared `retryable: false`.
pub const UPLOAD_KEY_IN_USE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/blobs/upload/begin:uploadKeyInUse",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `rooms/blobs/upload/begin:tooManyUploads`
///
/// The authenticated party already holds as many open uploads as the host allows one party. Commit, abort or let one lapse, then begin again. `details.maxOpen`, when present, states the host's limit.
///
/// Declared `retryable: true`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const TOO_MANY_UPLOADS: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "rooms/blobs/upload/begin:tooManyUploads",
retryable: true,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"maxOpen\":{\"minimum\":1,\"type\":\"integer\"}},\"type\":\"object\"}",
),
};
}