//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vtc/backup/import`. Version: `0.1`.
#[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())
}
}
}
///Unencrypted metadata plus the encrypted payload. The KDF and cipher parameters travel in the clear so a reader can decrypt without knowing this specification's defaults.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "BackupEnvelope",
/// "description": "Unencrypted metadata plus the encrypted payload. The KDF and cipher parameters travel in the clear so a reader can decrypt without knowing this specification's defaults.",
/// "type": "object",
/// "required": [
/// "ciphertext",
/// "createdAt",
/// "encryption",
/// "format",
/// "includesAudit",
/// "kdf",
/// "sourceVersion",
/// "version"
/// ],
/// "properties": {
/// "ciphertext": {
/// "description": "base64url(AEAD(JSON(payload))).",
/// "type": "string",
/// "minLength": 1
/// },
/// "createdAt": {
/// "description": "When the export was taken.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "encryption": {
/// "$ref": "#/definitions/EncryptionParams"
/// },
/// "format": {
/// "description": "Envelope format tag, e.g. `vtc-backup-v1`.",
/// "type": "string",
/// "minLength": 1
/// },
/// "includesAudit": {
/// "description": "Whether the audit log and its signed checkpoints are inside `ciphertext`.",
/// "type": "boolean"
/// },
/// "kdf": {
/// "$ref": "#/definitions/KdfParams"
/// },
/// "sourceDid": {
/// "description": "DID of the community exported from, when it has one. An import cross-checks this against the running community.",
/// "type": [
/// "string",
/// "null"
/// ]
/// },
/// "sourceVersion": {
/// "description": "Version of the software that produced the envelope.",
/// "type": "string",
/// "minLength": 1
/// },
/// "version": {
/// "description": "Envelope schema version.",
/// "type": "integer",
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "backupEnvelope"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct BackupEnvelope {
///base64url(AEAD(JSON(payload))).
pub ciphertext: BackupEnvelopeCiphertext,
///When the export was taken.
#[serde(rename = "createdAt")]
pub created_at: ::chrono::DateTime<::chrono::offset::Utc>,
pub encryption: EncryptionParams,
///Envelope format tag, e.g. `vtc-backup-v1`.
pub format: BackupEnvelopeFormat,
///Whether the audit log and its signed checkpoints are inside `ciphertext`.
#[serde(rename = "includesAudit")]
pub includes_audit: bool,
pub kdf: KdfParams,
///DID of the community exported from, when it has one. An import cross-checks this against the running community.
#[serde(
rename = "sourceDid",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub source_did: ::std::option::Option<::std::string::String>,
///Version of the software that produced the envelope.
#[serde(rename = "sourceVersion")]
pub source_version: BackupEnvelopeSourceVersion,
///Envelope schema version.
pub version: ::std::num::NonZeroU64,
}
///base64url(AEAD(JSON(payload))).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "base64url(AEAD(JSON(payload))).",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct BackupEnvelopeCiphertext(::std::string::String);
impl ::std::ops::Deref for BackupEnvelopeCiphertext {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<BackupEnvelopeCiphertext> for ::std::string::String {
fn from(value: BackupEnvelopeCiphertext) -> Self {
value.0
}
}
impl ::std::str::FromStr for BackupEnvelopeCiphertext {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for BackupEnvelopeCiphertext {
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 BackupEnvelopeCiphertext {
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 BackupEnvelopeCiphertext {
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 BackupEnvelopeCiphertext {
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())
})
}
}
///Envelope format tag, e.g. `vtc-backup-v1`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Envelope format tag, e.g. `vtc-backup-v1`.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct BackupEnvelopeFormat(::std::string::String);
impl ::std::ops::Deref for BackupEnvelopeFormat {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<BackupEnvelopeFormat> for ::std::string::String {
fn from(value: BackupEnvelopeFormat) -> Self {
value.0
}
}
impl ::std::str::FromStr for BackupEnvelopeFormat {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for BackupEnvelopeFormat {
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 BackupEnvelopeFormat {
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 BackupEnvelopeFormat {
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 BackupEnvelopeFormat {
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())
})
}
}
///Version of the software that produced the envelope.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Version of the software that produced the envelope.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct BackupEnvelopeSourceVersion(::std::string::String);
impl ::std::ops::Deref for BackupEnvelopeSourceVersion {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<BackupEnvelopeSourceVersion> for ::std::string::String {
fn from(value: BackupEnvelopeSourceVersion) -> Self {
value.0
}
}
impl ::std::str::FromStr for BackupEnvelopeSourceVersion {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for BackupEnvelopeSourceVersion {
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 BackupEnvelopeSourceVersion {
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 BackupEnvelopeSourceVersion {
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 BackupEnvelopeSourceVersion {
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())
})
}
}
///`EncryptionParams`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "EncryptionParams",
/// "type": "object",
/// "required": [
/// "algorithm",
/// "nonce"
/// ],
/// "properties": {
/// "algorithm": {
/// "description": "AEAD identifier, e.g. `aes-256-gcm`.",
/// "type": "string",
/// "minLength": 1
/// },
/// "nonce": {
/// "description": "base64url, 12 bytes.",
/// "type": "string",
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "encryptionParams"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct EncryptionParams {
///AEAD identifier, e.g. `aes-256-gcm`.
pub algorithm: EncryptionParamsAlgorithm,
///base64url, 12 bytes.
pub nonce: EncryptionParamsNonce,
}
///AEAD identifier, e.g. `aes-256-gcm`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "AEAD identifier, e.g. `aes-256-gcm`.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct EncryptionParamsAlgorithm(::std::string::String);
impl ::std::ops::Deref for EncryptionParamsAlgorithm {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<EncryptionParamsAlgorithm> for ::std::string::String {
fn from(value: EncryptionParamsAlgorithm) -> Self {
value.0
}
}
impl ::std::str::FromStr for EncryptionParamsAlgorithm {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for EncryptionParamsAlgorithm {
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 EncryptionParamsAlgorithm {
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 EncryptionParamsAlgorithm {
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 EncryptionParamsAlgorithm {
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())
})
}
}
///base64url, 12 bytes.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "base64url, 12 bytes.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct EncryptionParamsNonce(::std::string::String);
impl ::std::ops::Deref for EncryptionParamsNonce {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<EncryptionParamsNonce> for ::std::string::String {
fn from(value: EncryptionParamsNonce) -> Self {
value.0
}
}
impl ::std::str::FromStr for EncryptionParamsNonce {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for EncryptionParamsNonce {
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 EncryptionParamsNonce {
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 EncryptionParamsNonce {
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 EncryptionParamsNonce {
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())
})
}
}
///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())
})
}
}
///`KdfParams`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "KdfParams",
/// "type": "object",
/// "required": [
/// "algorithm",
/// "mCost",
/// "pCost",
/// "salt",
/// "tCost"
/// ],
/// "properties": {
/// "algorithm": {
/// "description": "KDF identifier, e.g. `argon2id`.",
/// "type": "string",
/// "minLength": 1
/// },
/// "mCost": {
/// "description": "Argon2id memory cost (KiB).",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "pCost": {
/// "description": "Argon2id parallelism.",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "salt": {
/// "description": "base64url, 32 bytes.",
/// "type": "string",
/// "minLength": 1
/// },
/// "tCost": {
/// "description": "Argon2id time cost (iterations).",
/// "type": "integer",
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "kdfParams"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct KdfParams {
///KDF identifier, e.g. `argon2id`.
pub algorithm: KdfParamsAlgorithm,
///Argon2id memory cost (KiB).
#[serde(rename = "mCost")]
pub m_cost: ::std::num::NonZeroU64,
///Argon2id parallelism.
#[serde(rename = "pCost")]
pub p_cost: ::std::num::NonZeroU64,
///base64url, 32 bytes.
pub salt: KdfParamsSalt,
///Argon2id time cost (iterations).
#[serde(rename = "tCost")]
pub t_cost: ::std::num::NonZeroU64,
}
///KDF identifier, e.g. `argon2id`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "KDF identifier, e.g. `argon2id`.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct KdfParamsAlgorithm(::std::string::String);
impl ::std::ops::Deref for KdfParamsAlgorithm {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<KdfParamsAlgorithm> for ::std::string::String {
fn from(value: KdfParamsAlgorithm) -> Self {
value.0
}
}
impl ::std::str::FromStr for KdfParamsAlgorithm {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for KdfParamsAlgorithm {
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 KdfParamsAlgorithm {
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 KdfParamsAlgorithm {
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 KdfParamsAlgorithm {
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())
})
}
}
///base64url, 32 bytes.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "base64url, 32 bytes.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct KdfParamsSalt(::std::string::String);
impl ::std::ops::Deref for KdfParamsSalt {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<KdfParamsSalt> for ::std::string::String {
fn from(value: KdfParamsSalt) -> Self {
value.0
}
}
impl ::std::str::FromStr for KdfParamsSalt {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for KdfParamsSalt {
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 KdfParamsSalt {
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 KdfParamsSalt {
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 KdfParamsSalt {
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())
})
}
}
///`Payload`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/vtc/backup/import/0.1",
/// "title": "Payload",
/// "type": "object",
/// "required": [
/// "backup",
/// "password"
/// ],
/// "properties": {
/// "backup": {
/// "description": "The envelope returned by `vtc/backup/export`.",
/// "$ref": "#/definitions/BackupEnvelope"
/// },
/// "confirm": {
/// "description": "`false` (default) previews: row counts, no mutation. `true` clears the backed-up keyspaces and applies the backup.",
/// "default": false,
/// "type": "boolean"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "password": {
/// "description": "The passphrase the envelope was encrypted under.",
/// "type": "string",
/// "minLength": 12
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Payload {
///The envelope returned by `vtc/backup/export`.
pub backup: BackupEnvelope,
///`false` (default) previews: row counts, no mutation. `true` clears the backed-up keyspaces and applies the backup.
#[serde(default)]
pub confirm: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///The passphrase the envelope was encrypted under.
pub password: PayloadPassword,
}
///The passphrase the envelope was encrypted under.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The passphrase the envelope was encrypted under.",
/// "type": "string",
/// "minLength": 12
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadPassword(::std::string::String);
impl ::std::ops::Deref for PayloadPassword {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadPassword> for ::std::string::String {
fn from(value: PayloadPassword) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadPassword {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 12usize {
return Err("shorter than 12 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadPassword {
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 PayloadPassword {
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 PayloadPassword {
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 PayloadPassword {
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())
})
}
}
///`Response`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "type": "object",
/// "required": [
/// "counts",
/// "message",
/// "status"
/// ],
/// "properties": {
/// "counts": {
/// "description": "Per-keyspace row counts written, or that would be written on a preview.",
/// "type": "object",
/// "additionalProperties": {
/// "type": "integer",
/// "minimum": 0.0
/// }
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "message": {
/// "description": "Human-readable summary for the operator.",
/// "type": "string"
/// },
/// "sourceDid": {
/// "description": "The community DID the envelope was taken from.",
/// "type": [
/// "string",
/// "null"
/// ]
/// },
/// "status": {
/// "description": "`preview` when `confirm` was not set; `imported` after a restore.",
/// "type": "string",
/// "enum": [
/// "preview",
/// "imported"
/// ]
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Response {
///Per-keyspace row counts written, or that would be written on a preview.
pub counts: ::std::collections::HashMap<::std::string::String, u64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Human-readable summary for the operator.
pub message: ::std::string::String,
///The community DID the envelope was taken from.
#[serde(
rename = "sourceDid",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub source_did: ::std::option::Option<::std::string::String>,
///`preview` when `confirm` was not set; `imported` after a restore.
pub status: ResponseStatus,
}
///`preview` when `confirm` was not set; `imported` after a restore.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "`preview` when `confirm` was not set; `imported` after a restore.",
/// "type": "string",
/// "enum": [
/// "preview",
/// "imported"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum ResponseStatus {
#[serde(rename = "preview")]
Preview,
#[serde(rename = "imported")]
Imported,
}
impl ::std::fmt::Display for ResponseStatus {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Preview => f.write_str("preview"),
Self::Imported => f.write_str("imported"),
}
}
}
impl ::std::str::FromStr for ResponseStatus {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"preview" => Ok(Self::Preview),
"imported" => Ok(Self::Imported),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ResponseStatus {
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 ResponseStatus {
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 ResponseStatus {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/backup/import/0.1";
const IS_PROOF_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
}
impl crate::Payload for Response {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/backup/import/0.1#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
}
#[cfg(feature = "validate")]
impl crate::validate::ValidatedPayload for Payload {
const SCHEMA_JSON: &'static str = "{\n \"$defs\": {\n \"BackupEnvelope\": {\n \"$anchor\": \"backupEnvelope\",\n \"additionalProperties\": false,\n \"description\": \"Unencrypted metadata plus the encrypted payload. The KDF and cipher parameters travel in the clear so a reader can decrypt without knowing this specification's defaults.\",\n \"properties\": {\n \"ciphertext\": {\n \"description\": \"base64url(AEAD(JSON(payload))).\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"createdAt\": {\n \"description\": \"When the export was taken.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"encryption\": {\n \"$ref\": \"#/$defs/EncryptionParams\"\n },\n \"format\": {\n \"description\": \"Envelope format tag, e.g. `vtc-backup-v1`.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"includesAudit\": {\n \"description\": \"Whether the audit log and its signed checkpoints are inside `ciphertext`.\",\n \"type\": \"boolean\"\n },\n \"kdf\": {\n \"$ref\": \"#/$defs/KdfParams\"\n },\n \"sourceDid\": {\n \"description\": \"DID of the community exported from, when it has one. An import cross-checks this against the running community.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"sourceVersion\": {\n \"description\": \"Version of the software that produced the envelope.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"version\": {\n \"description\": \"Envelope schema version.\",\n \"minimum\": 1,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"version\",\n \"format\",\n \"createdAt\",\n \"sourceVersion\",\n \"kdf\",\n \"encryption\",\n \"includesAudit\",\n \"ciphertext\"\n ],\n \"title\": \"BackupEnvelope\",\n \"type\": \"object\"\n },\n \"EncryptionParams\": {\n \"$anchor\": \"encryptionParams\",\n \"additionalProperties\": false,\n \"properties\": {\n \"algorithm\": {\n \"description\": \"AEAD identifier, e.g. `aes-256-gcm`.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"nonce\": {\n \"description\": \"base64url, 12 bytes.\",\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"algorithm\",\n \"nonce\"\n ],\n \"title\": \"EncryptionParams\",\n \"type\": \"object\"\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 \"KdfParams\": {\n \"$anchor\": \"kdfParams\",\n \"additionalProperties\": false,\n \"properties\": {\n \"algorithm\": {\n \"description\": \"KDF identifier, e.g. `argon2id`.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"mCost\": {\n \"description\": \"Argon2id memory cost (KiB).\",\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"pCost\": {\n \"description\": \"Argon2id parallelism.\",\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"salt\": {\n \"description\": \"base64url, 32 bytes.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"tCost\": {\n \"description\": \"Argon2id time cost (iterations).\",\n \"minimum\": 1,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"algorithm\",\n \"salt\",\n \"mCost\",\n \"tCost\",\n \"pCost\"\n ],\n \"title\": \"KdfParams\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"properties\": {\n \"counts\": {\n \"additionalProperties\": {\n \"minimum\": 0,\n \"type\": \"integer\"\n },\n \"description\": \"Per-keyspace row counts written, or that would be written on a preview.\",\n \"type\": \"object\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"message\": {\n \"description\": \"Human-readable summary for the operator.\",\n \"type\": \"string\"\n },\n \"sourceDid\": {\n \"description\": \"The community DID the envelope was taken from.\",\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"status\": {\n \"description\": \"`preview` when `confirm` was not set; `imported` after a restore.\",\n \"enum\": [\n \"preview\",\n \"imported\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"status\",\n \"counts\",\n \"message\"\n ],\n \"title\": \"VTC Backup Import — response payload\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/vtc/backup/import/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"properties\": {\n \"backup\": {\n \"$ref\": \"#/$defs/BackupEnvelope\",\n \"description\": \"The envelope returned by `vtc/backup/export`.\"\n },\n \"confirm\": {\n \"default\": false,\n \"description\": \"`false` (default) previews: row counts, no mutation. `true` clears the backed-up keyspaces and applies the backup.\",\n \"type\": \"boolean\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"password\": {\n \"description\": \"The passphrase the envelope was encrypted under.\",\n \"minLength\": 12,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"backup\",\n \"password\"\n ],\n \"title\": \"VTC Backup Import — payload\",\n \"type\": \"object\"\n}\n";
}
#[cfg(test)]
mod conformance {
//! Round-trip tests harvested from the spec's `spec.md`,
//! plus a `rejects_invalid_examples` test for any fixtures
//! in `payload.invalid-examples.json` (validate feature).
/// Each fixture in `payload.invalid-examples.json` MUST be
/// rejected by at least one of: serde deserialization, or
/// JSON-Schema validation under the `validate` feature. The
/// fixture file documents the producer-side bug class that
/// each payload exemplifies; this generated test pins it.
#[cfg(feature = "validate")]
#[test]
fn rejects_invalid_examples() {
use crate::validate::ValidatedPayload;
let fixtures: &[(&str, &str)] = &[
("`backup` and `password` are both required.", "{}"),
(
"`password` is required even when the envelope is present.",
"{\n \"backup\": {\n \"ciphertext\": \"Y3Q\",\n \"createdAt\": \"2026-07-26T10:00:00Z\",\n \"encryption\": {\n \"algorithm\": \"aes-256-gcm\",\n \"nonce\": \"bm9uY2U\"\n },\n \"format\": \"vtc-backup-v1\",\n \"includesAudit\": false,\n \"kdf\": {\n \"algorithm\": \"argon2id\",\n \"mCost\": 65536,\n \"pCost\": 4,\n \"salt\": \"c2FsdA\",\n \"tCost\": 3\n },\n \"sourceVersion\": \"0.11.28\",\n \"version\": 1\n }\n}",
),
(
"`status` is a closed enum; `restored` is not a member.",
"{\n \"backup\": {},\n \"password\": \"twelve-char-pw!!\",\n \"status\": \"restored\"\n}",
),
(
"Unknown top-level member is rejected.",
"{\n \"__x__\": true,\n \"backup\": {},\n \"password\": \"twelve-char-pw!!\"\n}",
),
];
for (i, (note, raw)) in fixtures.iter().enumerate() {
let value: serde_json::Value = match serde_json::from_str(raw) {
Ok(v) => v,
Err(_) => continue,
};
let serde_ok = serde_json::from_value::<super::Payload>(value.clone()).is_ok();
let schema_ok = super::Payload::validate_value(&value).is_ok();
assert!(
!(serde_ok && schema_ok),
"invalid-example #{} ({:?}) was accepted by both serde and JSON Schema; \
the fixture's stated failure class is no longer caught:\n{}",
i + 1,
note,
raw
);
}
}
}