//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vtc/storage/credentials/set`. Version: `0.3`.
/// Why this specification version is deprecated (SPEC "Superseded and
/// Withdrawn Versions"), or `None` while it is current.
pub const DEPRECATION: Option<&str> = None;
#[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())
}
}
}
///Whether a config can authenticate now, and how — never the credential. `status`: `vta-account`, `ambient` and `sealed` are usable; `required` means the config cannot store until a credential is provided (a `sealed` config never set, or set before a restore, which carries no credential); `notAllowed` means the config's mode is one the community's host configuration no longer permits, so it cannot store until its `auth` is changed; `none` is a `local` config.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct CredentialState {
///Present with `vta-account`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub account: ::std::option::Option<ExternalAccountId>,
///Present with `vta-account`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub context: ::std::option::Option<ExternalAccountContextId>,
///Present with `sealed`: a digest identifying the stored credential, so an administrator can tell which one is set without anyone being able to read it. Computed over the credential with a community-held key, never a bare hash of a low-entropy secret.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub fingerprint: ::std::option::Option<DigestMultibase>,
///Present with `vta-account`, `ambient` and `sealed`: how many rooms the community currently holds a live room-scoped credential for.
#[serde(
rename = "roomsHoldingCredentials",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub rooms_holding_credentials: ::std::option::Option<u64>,
///Present with `sealed`: when the stored credential was last set.
#[serde(
rename = "setAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub set_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
pub status: CredentialStateStatus,
}
impl CredentialState {
pub fn builder() -> builder::CredentialState {
Default::default()
}
}
///`CredentialStateStatus`
///
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum CredentialStateStatus {
#[serde(rename = "vta-account")]
VtaAccount,
#[serde(rename = "ambient")]
Ambient,
#[serde(rename = "sealed")]
Sealed,
#[serde(rename = "required")]
Required,
#[serde(rename = "notAllowed")]
NotAllowed,
#[serde(rename = "none")]
None,
}
impl ::std::fmt::Display for CredentialStateStatus {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::VtaAccount => f.write_str("vta-account"),
Self::Ambient => f.write_str("ambient"),
Self::Sealed => f.write_str("sealed"),
Self::Required => f.write_str("required"),
Self::NotAllowed => f.write_str("notAllowed"),
Self::None => f.write_str("none"),
}
}
}
impl ::std::str::FromStr for CredentialStateStatus {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"vta-account" => Ok(Self::VtaAccount),
"ambient" => Ok(Self::Ambient),
"sealed" => Ok(Self::Sealed),
"required" => Ok(Self::Required),
"notAllowed" => Ok(Self::NotAllowed),
"none" => Ok(Self::None),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for CredentialStateStatus {
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 CredentialStateStatus {
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 CredentialStateStatus {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
/**
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`.*/
///
#[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())
})
}
}
///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.
///
#[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`
///
#[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())
})
}
}
///The context at the community's VTA that owns an external account. The VTA names an account by its context and its identifier, and decides who may use it by act scope in that context: the community must hold `external-auth-use` there, and be bound to the account, for the account to issue it anything. Naming a context grants nothing; the VTA's binding is the gate.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExternalAccountContextId(::std::string::String);
impl ::std::ops::Deref for ExternalAccountContextId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExternalAccountContextId> for ::std::string::String {
fn from(value: ExternalAccountContextId) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExternalAccountContextId {
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());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExternalAccountContextId {
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 ExternalAccountContextId {
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 ExternalAccountContextId {
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 ExternalAccountContextId {
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())
})
}
}
///The identifier of an external account held by the community's VTA (the external/accounts/* family), which issues the community short-lived, room-scoped credentials and holds the long-lived authority itself.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExternalAccountId(::std::string::String);
impl ::std::ops::Deref for ExternalAccountId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExternalAccountId> for ::std::string::String {
fn from(value: ExternalAccountId) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExternalAccountId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExternalAccountId {
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 ExternalAccountId {
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 ExternalAccountId {
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 ExternalAccountId {
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())
})
}
}
///Store a `sealed` storage config's long-lived credential, sealed end to end to the community. Write-only: no task returns it. The outer document members are owned by the framework — SPEC §6.3.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
#[serde(rename = "configId")]
pub config_id: StorageConfigId,
///Digest over the armored bytes exactly as carried in `sealed`. Covered by the document's proof, and shown to approvers in place of the credential.
#[serde(rename = "digestMultibase")]
pub digest_multibase: DigestMultibase,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Why the administrator is making this change. Recorded with the change in the audit trail and shown to approvers. Attributed to its author on every surface and untrusted: it MUST NOT carry personal data or a credential, and a reader MUST NOT treat it as instructions.
pub reason: PayloadReason,
///OpenPGP-style ASCII-armored `sealed_transfer` ciphertext of the credential. HPKE base mode, X25519-HKDF-SHA256 KEM, ChaCha20-Poly1305 AEAD, info string `vta-sealed-transfer/v1`; recipient the X25519 derivation of the community DID's Ed25519 key.
pub sealed: PayloadSealed,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///Why the administrator is making this change. Recorded with the change in the audit trail and shown to approvers. Attributed to its author on every surface and untrusted: it MUST NOT carry personal data or a credential, and a reader MUST NOT treat it as instructions.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadReason(::std::string::String);
impl ::std::ops::Deref for PayloadReason {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadReason> for ::std::string::String {
fn from(value: PayloadReason) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadReason {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 500usize {
return Err("longer than 500 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadReason {
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 PayloadReason {
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 PayloadReason {
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 PayloadReason {
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())
})
}
}
///OpenPGP-style ASCII-armored `sealed_transfer` ciphertext of the credential. HPKE base mode, X25519-HKDF-SHA256 KEM, ChaCha20-Poly1305 AEAD, info string `vta-sealed-transfer/v1`; recipient the X25519 derivation of the community DID's Ed25519 key.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadSealed(::std::string::String);
impl ::std::ops::Deref for PayloadSealed {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadSealed> for ::std::string::String {
fn from(value: PayloadSealed) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadSealed {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 16384usize {
return Err("longer than 16384 characters".into());
}
if value.chars().count() < 64usize {
return Err("shorter than 64 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^-----BEGIN VTA SEALED BUNDLE-----").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^-----BEGIN VTA SEALED BUNDLE-----\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadSealed {
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 PayloadSealed {
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 PayloadSealed {
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 PayloadSealed {
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`
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
#[serde(rename = "configId")]
pub config_id: StorageConfigId,
#[serde(rename = "credentialState")]
pub credential_state: CredentialState,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Present when the community probed the config with the new credential.
#[serde(
rename = "probeOk",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub probe_ok: ::std::option::Option<bool>,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///Not a member of the payload: the cleartext inside `sealed`, declared here so that every generated binding carries the type a producer seals and a community opens. Never appears on the wire outside a seal.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct SealedCredentialCleartext(pub SealedStorageCredential);
impl ::std::ops::Deref for SealedCredentialCleartext {
type Target = SealedStorageCredential;
fn deref(&self) -> &SealedStorageCredential {
&self.0
}
}
impl ::std::convert::From<SealedCredentialCleartext> for SealedStorageCredential {
fn from(value: SealedCredentialCleartext) -> Self {
value.0
}
}
impl ::std::convert::From<SealedStorageCredential> for SealedCredentialCleartext {
fn from(value: SealedStorageCredential) -> Self {
Self(value)
}
}
///The cleartext inside the sealed block of vtc/storage/credentials/set/0.2, discriminated by `kind`, which must equal the config's kind. It names the config it is for inside the seal, so that a block captured on its way to one config cannot be replayed into another: a community MUST refuse a block whose `configId` differs from the request's. Never appears on the wire outside a seal, and is never returned. A `local` config has no credential.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(tag = "kind", deny_unknown_fields)]
#[non_exhaustive]
pub enum SealedStorageCredential {
///An S3 access-key pair: an AWS IAM user's key, or an S3-compatible store's (R2, B2, MinIO).
#[serde(rename = "s3")]
S3 {
///The access key's identifier.
#[serde(rename = "accessKeyId")]
access_key_id: SealedStorageCredentialAccessKeyId,
#[serde(rename = "configId")]
config_id: StorageConfigId,
///The secret half.
#[serde(rename = "secretAccessKey")]
secret_access_key: SealedStorageCredentialSecretAccessKey,
///Present when the pair is a temporary one that needs it. A temporary pair stops working at its own expiry, after which the config reports failures until another is set; it is accepted for evaluation, never recommended.
#[serde(
rename = "sessionToken",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
session_token: ::std::option::Option<SealedStorageCredentialSessionToken>,
},
///A Google Cloud service-account key.
#[serde(rename = "gcs")]
Gcs {
#[serde(rename = "configId")]
config_id: StorageConfigId,
#[serde(rename = "serviceAccountKey")]
service_account_key: SealedStorageCredentialServiceAccountKey,
},
///The Sui key that pays for a `walrus` config's storage.
#[serde(rename = "walrus")]
Walrus {
#[serde(rename = "configId")]
config_id: StorageConfigId,
///The private key in Sui's Bech32 `suiprivkey` encoding (SIP-15), which carries its signature scheme.
#[serde(rename = "suiPrivateKey")]
sui_private_key: SealedStorageCredentialSuiPrivateKey,
},
}
///The access key's identifier.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SealedStorageCredentialAccessKeyId(::std::string::String);
impl ::std::ops::Deref for SealedStorageCredentialAccessKeyId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SealedStorageCredentialAccessKeyId> for ::std::string::String {
fn from(value: SealedStorageCredentialAccessKeyId) -> Self {
value.0
}
}
impl ::std::str::FromStr for SealedStorageCredentialAccessKeyId {
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());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[A-Za-z0-9]+$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[A-Za-z0-9]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SealedStorageCredentialAccessKeyId {
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 SealedStorageCredentialAccessKeyId {
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 SealedStorageCredentialAccessKeyId {
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 SealedStorageCredentialAccessKeyId {
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())
})
}
}
///The secret half.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SealedStorageCredentialSecretAccessKey(::std::string::String);
impl ::std::ops::Deref for SealedStorageCredentialSecretAccessKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SealedStorageCredentialSecretAccessKey> for ::std::string::String {
fn from(value: SealedStorageCredentialSecretAccessKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for SealedStorageCredentialSecretAccessKey {
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());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[!-~]+$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[!-~]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SealedStorageCredentialSecretAccessKey {
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 SealedStorageCredentialSecretAccessKey {
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 SealedStorageCredentialSecretAccessKey {
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 SealedStorageCredentialSecretAccessKey {
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())
})
}
}
///The service-account key exactly as Google issues it — the JSON key file, as an object, its member names carried verbatim in Google's own snake_case. Members beyond the four required (`project_id`, `client_id`, `token_uri`, `universe_domain` and the rest) are permitted, and a community MAY discard them: it authenticates with `client_email`, `private_key_id` and `private_key` alone, against Google's own token endpoint, and never against a `token_uri` the key file names.
///
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[non_exhaustive]
pub struct SealedStorageCredentialServiceAccountKey {
pub client_email: SealedStorageCredentialServiceAccountKeyClientEmail,
///The PKCS #8 PEM private key.
pub private_key: SealedStorageCredentialServiceAccountKeyPrivateKey,
pub private_key_id: SealedStorageCredentialServiceAccountKeyPrivateKeyId,
#[serde(rename = "type")]
pub type_: ::serde_json::Value,
}
impl SealedStorageCredentialServiceAccountKey {
pub fn builder() -> builder::SealedStorageCredentialServiceAccountKey {
Default::default()
}
}
///`SealedStorageCredentialServiceAccountKeyClientEmail`
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SealedStorageCredentialServiceAccountKeyClientEmail(::std::string::String);
impl ::std::ops::Deref for SealedStorageCredentialServiceAccountKeyClientEmail {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SealedStorageCredentialServiceAccountKeyClientEmail>
for ::std::string::String
{
fn from(value: SealedStorageCredentialServiceAccountKeyClientEmail) -> Self {
value.0
}
}
impl ::std::str::FromStr for SealedStorageCredentialServiceAccountKeyClientEmail {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 320usize {
return Err("longer than 320 characters".into());
}
if value.chars().count() < 3usize {
return Err("shorter than 3 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[^@\\s]+@[^@\\s]+$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[^@\\s]+@[^@\\s]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SealedStorageCredentialServiceAccountKeyClientEmail {
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 SealedStorageCredentialServiceAccountKeyClientEmail
{
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 SealedStorageCredentialServiceAccountKeyClientEmail
{
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 SealedStorageCredentialServiceAccountKeyClientEmail {
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())
})
}
}
///The PKCS #8 PEM private key.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SealedStorageCredentialServiceAccountKeyPrivateKey(::std::string::String);
impl ::std::ops::Deref for SealedStorageCredentialServiceAccountKeyPrivateKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SealedStorageCredentialServiceAccountKeyPrivateKey>
for ::std::string::String
{
fn from(value: SealedStorageCredentialServiceAccountKeyPrivateKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for SealedStorageCredentialServiceAccountKeyPrivateKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 8192usize {
return Err("longer than 8192 characters".into());
}
if value.chars().count() < 64usize {
return Err("shorter than 64 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^-----BEGIN PRIVATE KEY-----").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^-----BEGIN PRIVATE KEY-----\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SealedStorageCredentialServiceAccountKeyPrivateKey {
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 SealedStorageCredentialServiceAccountKeyPrivateKey
{
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 SealedStorageCredentialServiceAccountKeyPrivateKey
{
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 SealedStorageCredentialServiceAccountKeyPrivateKey {
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())
})
}
}
///`SealedStorageCredentialServiceAccountKeyPrivateKeyId`
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SealedStorageCredentialServiceAccountKeyPrivateKeyId(::std::string::String);
impl ::std::ops::Deref for SealedStorageCredentialServiceAccountKeyPrivateKeyId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SealedStorageCredentialServiceAccountKeyPrivateKeyId>
for ::std::string::String
{
fn from(value: SealedStorageCredentialServiceAccountKeyPrivateKeyId) -> Self {
value.0
}
}
impl ::std::str::FromStr for SealedStorageCredentialServiceAccountKeyPrivateKeyId {
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());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[A-Za-z0-9]+$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[A-Za-z0-9]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SealedStorageCredentialServiceAccountKeyPrivateKeyId {
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 SealedStorageCredentialServiceAccountKeyPrivateKeyId
{
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 SealedStorageCredentialServiceAccountKeyPrivateKeyId
{
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 SealedStorageCredentialServiceAccountKeyPrivateKeyId {
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())
})
}
}
///Present when the pair is a temporary one that needs it. A temporary pair stops working at its own expiry, after which the config reports failures until another is set; it is accepted for evaluation, never recommended.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SealedStorageCredentialSessionToken(::std::string::String);
impl ::std::ops::Deref for SealedStorageCredentialSessionToken {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SealedStorageCredentialSessionToken> for ::std::string::String {
fn from(value: SealedStorageCredentialSessionToken) -> Self {
value.0
}
}
impl ::std::str::FromStr for SealedStorageCredentialSessionToken {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 8192usize {
return Err("longer than 8192 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[!-~]+$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[!-~]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SealedStorageCredentialSessionToken {
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 SealedStorageCredentialSessionToken {
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 SealedStorageCredentialSessionToken {
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 SealedStorageCredentialSessionToken {
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())
})
}
}
///The private key in Sui's Bech32 `suiprivkey` encoding (SIP-15), which carries its signature scheme.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SealedStorageCredentialSuiPrivateKey(::std::string::String);
impl ::std::ops::Deref for SealedStorageCredentialSuiPrivateKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SealedStorageCredentialSuiPrivateKey> for ::std::string::String {
fn from(value: SealedStorageCredentialSuiPrivateKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for SealedStorageCredentialSuiPrivateKey {
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());
}
if value.chars().count() < 20usize {
return Err("shorter than 20 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^suiprivkey1[02-9ac-hj-np-z]+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^suiprivkey1[02-9ac-hj-np-z]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SealedStorageCredentialSuiPrivateKey {
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 SealedStorageCredentialSuiPrivateKey {
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 SealedStorageCredentialSuiPrivateKey {
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 SealedStorageCredentialSuiPrivateKey {
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 storage config's identifier, chosen by the administrator who creates it and immutable thereafter: lowercase letters, digits and inner hyphens, at most 64 characters (`eu-s3-primary`, `walrus-main`). Unique within the community. Never reused after a config is retired, because blobs name their config by this value.
///
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct StorageConfigId(::std::string::String);
impl ::std::ops::Deref for StorageConfigId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<StorageConfigId> for ::std::string::String {
fn from(value: StorageConfigId) -> Self {
value.0
}
}
impl ::std::str::FromStr for StorageConfigId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for StorageConfigId {
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 StorageConfigId {
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 StorageConfigId {
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 StorageConfigId {
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())
})
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct CredentialState {
account: ::std::result::Result<
::std::option::Option<super::ExternalAccountId>,
::std::string::String,
>,
context: ::std::result::Result<
::std::option::Option<super::ExternalAccountContextId>,
::std::string::String,
>,
fingerprint: ::std::result::Result<
::std::option::Option<super::DigestMultibase>,
::std::string::String,
>,
rooms_holding_credentials:
::std::result::Result<::std::option::Option<u64>, ::std::string::String>,
set_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
status: ::std::result::Result<super::CredentialStateStatus, ::std::string::String>,
}
impl ::std::default::Default for CredentialState {
fn default() -> Self {
Self {
account: Ok(Default::default()),
context: Ok(Default::default()),
fingerprint: Ok(Default::default()),
rooms_holding_credentials: Ok(Default::default()),
set_at: Ok(Default::default()),
status: Err("no value supplied for status".to_string()),
}
}
}
impl CredentialState {
pub fn account<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ExternalAccountId>>,
T::Error: ::std::fmt::Display,
{
self.account = value
.try_into()
.map_err(|e| format!("error converting supplied value for account: {e}"));
self
}
pub fn context<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ExternalAccountContextId>>,
T::Error: ::std::fmt::Display,
{
self.context = value
.try_into()
.map_err(|e| format!("error converting supplied value for context: {e}"));
self
}
pub fn fingerprint<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::DigestMultibase>>,
T::Error: ::std::fmt::Display,
{
self.fingerprint = value
.try_into()
.map_err(|e| format!("error converting supplied value for fingerprint: {e}"));
self
}
pub fn rooms_holding_credentials<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<u64>>,
T::Error: ::std::fmt::Display,
{
self.rooms_holding_credentials = value.try_into().map_err(|e| {
format!("error converting supplied value for rooms_holding_credentials: {e}")
});
self
}
pub fn set_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.set_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for set_at: {e}"));
self
}
pub fn status<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::CredentialStateStatus>,
T::Error: ::std::fmt::Display,
{
self.status = value
.try_into()
.map_err(|e| format!("error converting supplied value for status: {e}"));
self
}
}
impl ::std::convert::TryFrom<CredentialState> for super::CredentialState {
type Error = super::error::ConversionError;
fn try_from(
value: CredentialState,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
account: value.account?,
context: value.context?,
fingerprint: value.fingerprint?,
rooms_holding_credentials: value.rooms_holding_credentials?,
set_at: value.set_at?,
status: value.status?,
})
}
}
impl ::std::convert::From<super::CredentialState> for CredentialState {
fn from(value: super::CredentialState) -> Self {
Self {
account: Ok(value.account),
context: Ok(value.context),
fingerprint: Ok(value.fingerprint),
rooms_holding_credentials: Ok(value.rooms_holding_credentials),
set_at: Ok(value.set_at),
status: Ok(value.status),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
config_id: ::std::result::Result<super::StorageConfigId, ::std::string::String>,
digest_multibase: ::std::result::Result<super::DigestMultibase, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
reason: ::std::result::Result<super::PayloadReason, ::std::string::String>,
sealed: ::std::result::Result<super::PayloadSealed, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
config_id: Err("no value supplied for config_id".to_string()),
digest_multibase: Err("no value supplied for digest_multibase".to_string()),
ext: Ok(Default::default()),
reason: Err("no value supplied for reason".to_string()),
sealed: Err("no value supplied for sealed".to_string()),
}
}
}
impl Payload {
pub fn config_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::StorageConfigId>,
T::Error: ::std::fmt::Display,
{
self.config_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for config_id: {e}"));
self
}
pub fn digest_multibase<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::DigestMultibase>,
T::Error: ::std::fmt::Display,
{
self.digest_multibase = value
.try_into()
.map_err(|e| format!("error converting supplied value for digest_multibase: {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 reason<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadReason>,
T::Error: ::std::fmt::Display,
{
self.reason = value
.try_into()
.map_err(|e| format!("error converting supplied value for reason: {e}"));
self
}
pub fn sealed<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadSealed>,
T::Error: ::std::fmt::Display,
{
self.sealed = value
.try_into()
.map_err(|e| format!("error converting supplied value for sealed: {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 {
config_id: value.config_id?,
digest_multibase: value.digest_multibase?,
ext: value.ext?,
reason: value.reason?,
sealed: value.sealed?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
config_id: Ok(value.config_id),
digest_multibase: Ok(value.digest_multibase),
ext: Ok(value.ext),
reason: Ok(value.reason),
sealed: Ok(value.sealed),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
config_id: ::std::result::Result<super::StorageConfigId, ::std::string::String>,
credential_state: ::std::result::Result<super::CredentialState, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
probe_ok: ::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
config_id: Err("no value supplied for config_id".to_string()),
credential_state: Err("no value supplied for credential_state".to_string()),
ext: Ok(Default::default()),
probe_ok: Ok(Default::default()),
}
}
}
impl Response {
pub fn config_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::StorageConfigId>,
T::Error: ::std::fmt::Display,
{
self.config_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for config_id: {e}"));
self
}
pub fn credential_state<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::CredentialState>,
T::Error: ::std::fmt::Display,
{
self.credential_state = value
.try_into()
.map_err(|e| format!("error converting supplied value for credential_state: {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 probe_ok<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.probe_ok = value
.try_into()
.map_err(|e| format!("error converting supplied value for probe_ok: {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 {
config_id: value.config_id?,
credential_state: value.credential_state?,
ext: value.ext?,
probe_ok: value.probe_ok?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
config_id: Ok(value.config_id),
credential_state: Ok(value.credential_state),
ext: Ok(value.ext),
probe_ok: Ok(value.probe_ok),
}
}
}
#[derive(Clone, Debug)]
pub struct SealedStorageCredentialServiceAccountKey {
client_email: ::std::result::Result<
super::SealedStorageCredentialServiceAccountKeyClientEmail,
::std::string::String,
>,
private_key: ::std::result::Result<
super::SealedStorageCredentialServiceAccountKeyPrivateKey,
::std::string::String,
>,
private_key_id: ::std::result::Result<
super::SealedStorageCredentialServiceAccountKeyPrivateKeyId,
::std::string::String,
>,
type_: ::std::result::Result<::serde_json::Value, ::std::string::String>,
}
impl ::std::default::Default for SealedStorageCredentialServiceAccountKey {
fn default() -> Self {
Self {
client_email: Err("no value supplied for client_email".to_string()),
private_key: Err("no value supplied for private_key".to_string()),
private_key_id: Err("no value supplied for private_key_id".to_string()),
type_: Err("no value supplied for type_".to_string()),
}
}
}
impl SealedStorageCredentialServiceAccountKey {
pub fn client_email<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::SealedStorageCredentialServiceAccountKeyClientEmail>,
T::Error: ::std::fmt::Display,
{
self.client_email = value
.try_into()
.map_err(|e| format!("error converting supplied value for client_email: {e}"));
self
}
pub fn private_key<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::SealedStorageCredentialServiceAccountKeyPrivateKey>,
T::Error: ::std::fmt::Display,
{
self.private_key = value
.try_into()
.map_err(|e| format!("error converting supplied value for private_key: {e}"));
self
}
pub fn private_key_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::SealedStorageCredentialServiceAccountKeyPrivateKeyId>,
T::Error: ::std::fmt::Display,
{
self.private_key_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for private_key_id: {e}"));
self
}
pub fn type_<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::serde_json::Value>,
T::Error: ::std::fmt::Display,
{
self.type_ = value
.try_into()
.map_err(|e| format!("error converting supplied value for type_: {e}"));
self
}
}
impl ::std::convert::TryFrom<SealedStorageCredentialServiceAccountKey>
for super::SealedStorageCredentialServiceAccountKey
{
type Error = super::error::ConversionError;
fn try_from(
value: SealedStorageCredentialServiceAccountKey,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
client_email: value.client_email?,
private_key: value.private_key?,
private_key_id: value.private_key_id?,
type_: value.type_?,
})
}
}
impl ::std::convert::From<super::SealedStorageCredentialServiceAccountKey>
for SealedStorageCredentialServiceAccountKey
{
fn from(value: super::SealedStorageCredentialServiceAccountKey) -> Self {
Self {
client_email: Ok(value.client_email),
private_key: Ok(value.private_key),
private_key_id: Ok(value.private_key_id),
type_: Ok(value.type_),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/storage/credentials/set/0.3";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\"$defs\":{\"CredentialState\":{\"additionalProperties\":false,\"description\":\"Whether a config can authenticate now, and how — never the credential. `status`: `vta-account`, `ambient` and `sealed` are usable; `required` means the config cannot store until a credential is provided (a `sealed` config never set, or set before a restore, which carries no credential); `notAllowed` means the config's mode is one the community's host configuration no longer permits, so it cannot store until its `auth` is changed; `none` is a `local` config.\",\"properties\":{\"account\":{\"$ref\":\"#/$defs/ExternalAccountId\",\"description\":\"Present with `vta-account`.\"},\"context\":{\"$ref\":\"#/$defs/ExternalAccountContextId\",\"description\":\"Present with `vta-account`.\"},\"fingerprint\":{\"$ref\":\"#/$defs/DigestMultibase\",\"description\":\"Present with `sealed`: a digest identifying the stored credential, so an administrator can tell which one is set without anyone being able to read it. Computed over the credential with a community-held key, never a bare hash of a low-entropy secret.\"},\"roomsHoldingCredentials\":{\"description\":\"Present with `vta-account`, `ambient` and `sealed`: how many rooms the community currently holds a live room-scoped credential for.\",\"minimum\":0,\"type\":\"integer\"},\"setAt\":{\"description\":\"Present with `sealed`: when the stored credential was last set.\",\"format\":\"date-time\",\"type\":\"string\"},\"status\":{\"enum\":[\"vta-account\",\"ambient\",\"sealed\",\"required\",\"notAllowed\",\"none\"],\"type\":\"string\"}},\"required\":[\"status\"],\"title\":\"CredentialState\",\"type\":\"object\"},\"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\"],\"minLength\":16,\"pattern\":\"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\"title\":\"DigestMultibase\",\"type\":\"string\"},\"Ext\":{\"additionalProperties\":true,\"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.\",\"minProperties\":1,\"propertyNames\":{\"pattern\":\"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"},\"title\":\"Ext\",\"type\":\"object\"},\"ExternalAccountContextId\":{\"description\":\"The context at the community's VTA that owns an external account. The VTA names an account by its context and its identifier, and decides who may use it by act scope in that context: the community must hold `external-auth-use` there, and be bound to the account, for the account to issue it anything. Naming a context grants nothing; the VTA's binding is the gate.\",\"maxLength\":256,\"minLength\":1,\"title\":\"ExternalAccountContextId\",\"type\":\"string\"},\"ExternalAccountId\":{\"description\":\"The identifier of an external account held by the community's VTA (the external/accounts/* family), which issues the community short-lived, room-scoped credentials and holds the long-lived authority itself.\",\"maxLength\":64,\"minLength\":1,\"pattern\":\"^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$\",\"title\":\"ExternalAccountId\",\"type\":\"string\"},\"Response\":{\"$anchor\":\"response\",\"additionalProperties\":false,\"properties\":{\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"credentialState\":{\"$ref\":\"#/$defs/CredentialState\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"probeOk\":{\"description\":\"Present when the community probed the config with the new credential.\",\"type\":\"boolean\"}},\"required\":[\"configId\",\"credentialState\"],\"title\":\"VTC Storage — Credentials — Set — response payload\",\"type\":\"object\"},\"SealedCredentialCleartext\":{\"$ref\":\"#/$defs/SealedStorageCredential\",\"description\":\"Not a member of the payload: the cleartext inside `sealed`, declared here so that every generated binding carries the type a producer seals and a community opens. Never appears on the wire outside a seal.\"},\"SealedStorageCredential\":{\"description\":\"The cleartext inside the sealed block of vtc/storage/credentials/set/0.2, discriminated by `kind`, which must equal the config's kind. It names the config it is for inside the seal, so that a block captured on its way to one config cannot be replayed into another: a community MUST refuse a block whose `configId` differs from the request's. Never appears on the wire outside a seal, and is never returned. A `local` config has no credential.\",\"oneOf\":[{\"additionalProperties\":false,\"description\":\"An S3 access-key pair: an AWS IAM user's key, or an S3-compatible store's (R2, B2, MinIO).\",\"properties\":{\"accessKeyId\":{\"description\":\"The access key's identifier.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^[A-Za-z0-9]+$\",\"type\":\"string\"},\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"kind\":{\"const\":\"s3\"},\"secretAccessKey\":{\"description\":\"The secret half.\",\"maxLength\":256,\"minLength\":1,\"pattern\":\"^[!-~]+$\",\"type\":\"string\"},\"sessionToken\":{\"description\":\"Present when the pair is a temporary one that needs it. A temporary pair stops working at its own expiry, after which the config reports failures until another is set; it is accepted for evaluation, never recommended.\",\"maxLength\":8192,\"minLength\":1,\"pattern\":\"^[!-~]+$\",\"type\":\"string\"}},\"required\":[\"kind\",\"configId\",\"accessKeyId\",\"secretAccessKey\"],\"type\":\"object\"},{\"additionalProperties\":false,\"description\":\"A Google Cloud service-account key.\",\"properties\":{\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"kind\":{\"const\":\"gcs\"},\"serviceAccountKey\":{\"additionalProperties\":true,\"description\":\"The service-account key exactly as Google issues it — the JSON key file, as an object, its member names carried verbatim in Google's own snake_case. Members beyond the four required (`project_id`, `client_id`, `token_uri`, `universe_domain` and the rest) are permitted, and a community MAY discard them: it authenticates with `client_email`, `private_key_id` and `private_key` alone, against Google's own token endpoint, and never against a `token_uri` the key file names.\",\"properties\":{\"client_email\":{\"maxLength\":320,\"minLength\":3,\"pattern\":\"^[^@\\\\s]+@[^@\\\\s]+$\",\"type\":\"string\"},\"private_key\":{\"description\":\"The PKCS #8 PEM private key.\",\"maxLength\":8192,\"minLength\":64,\"pattern\":\"^-----BEGIN PRIVATE KEY-----\",\"type\":\"string\"},\"private_key_id\":{\"maxLength\":128,\"minLength\":1,\"pattern\":\"^[A-Za-z0-9]+$\",\"type\":\"string\"},\"type\":{\"const\":\"service_account\"}},\"required\":[\"type\",\"client_email\",\"private_key_id\",\"private_key\"],\"type\":\"object\"}},\"required\":[\"kind\",\"configId\",\"serviceAccountKey\"],\"type\":\"object\"},{\"additionalProperties\":false,\"description\":\"The Sui key that pays for a `walrus` config's storage.\",\"properties\":{\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"kind\":{\"const\":\"walrus\"},\"suiPrivateKey\":{\"description\":\"The private key in Sui's Bech32 `suiprivkey` encoding (SIP-15), which carries its signature scheme.\",\"maxLength\":256,\"minLength\":20,\"pattern\":\"^suiprivkey1[02-9ac-hj-np-z]+$\",\"type\":\"string\"}},\"required\":[\"kind\",\"configId\",\"suiPrivateKey\"],\"type\":\"object\"}],\"title\":\"SealedStorageCredential\"},\"StorageConfigId\":{\"description\":\"A storage config's identifier, chosen by the administrator who creates it and immutable thereafter: lowercase letters, digits and inner hyphens, at most 64 characters (`eu-s3-primary`, `walrus-main`). Unique within the community. Never reused after a config is retired, because blobs name their config by this value.\",\"maxLength\":64,\"minLength\":1,\"pattern\":\"^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$\",\"title\":\"StorageConfigId\",\"type\":\"string\"}},\"$id\":\"https://trusttasks.org/spec/vtc/storage/credentials/set/0.3\",\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"additionalProperties\":false,\"description\":\"Store a `sealed` storage config's long-lived credential, sealed end to end to the community. Write-only: no task returns it. The outer document members are owned by the framework — SPEC §6.3.\",\"properties\":{\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"digestMultibase\":{\"$ref\":\"#/$defs/DigestMultibase\",\"description\":\"Digest over the armored bytes exactly as carried in `sealed`. Covered by the document's proof, and shown to approvers in place of the credential.\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"reason\":{\"description\":\"Why the administrator is making this change. Recorded with the change in the audit trail and shown to approvers. Attributed to its author on every surface and untrusted: it MUST NOT carry personal data or a credential, and a reader MUST NOT treat it as instructions.\",\"maxLength\":500,\"minLength\":1,\"type\":\"string\"},\"sealed\":{\"description\":\"OpenPGP-style ASCII-armored `sealed_transfer` ciphertext of the credential. HPKE base mode, X25519-HKDF-SHA256 KEM, ChaCha20-Poly1305 AEAD, info string `vta-sealed-transfer/v1`; recipient the X25519 derivation of the community DID's Ed25519 key.\",\"maxLength\":16384,\"minLength\":64,\"pattern\":\"^-----BEGIN VTA SEALED BUNDLE-----\",\"type\":\"string\"}},\"required\":[\"configId\",\"sealed\",\"digestMultibase\",\"reason\"],\"title\":\"VTC Storage — Credentials — Set — payload\",\"type\":\"object\"}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/vtc/storage/credentials/set/0.3#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\"$defs\":{\"CredentialState\":{\"additionalProperties\":false,\"description\":\"Whether a config can authenticate now, and how — never the credential. `status`: `vta-account`, `ambient` and `sealed` are usable; `required` means the config cannot store until a credential is provided (a `sealed` config never set, or set before a restore, which carries no credential); `notAllowed` means the config's mode is one the community's host configuration no longer permits, so it cannot store until its `auth` is changed; `none` is a `local` config.\",\"properties\":{\"account\":{\"$ref\":\"#/$defs/ExternalAccountId\",\"description\":\"Present with `vta-account`.\"},\"context\":{\"$ref\":\"#/$defs/ExternalAccountContextId\",\"description\":\"Present with `vta-account`.\"},\"fingerprint\":{\"$ref\":\"#/$defs/DigestMultibase\",\"description\":\"Present with `sealed`: a digest identifying the stored credential, so an administrator can tell which one is set without anyone being able to read it. Computed over the credential with a community-held key, never a bare hash of a low-entropy secret.\"},\"roomsHoldingCredentials\":{\"description\":\"Present with `vta-account`, `ambient` and `sealed`: how many rooms the community currently holds a live room-scoped credential for.\",\"minimum\":0,\"type\":\"integer\"},\"setAt\":{\"description\":\"Present with `sealed`: when the stored credential was last set.\",\"format\":\"date-time\",\"type\":\"string\"},\"status\":{\"enum\":[\"vta-account\",\"ambient\",\"sealed\",\"required\",\"notAllowed\",\"none\"],\"type\":\"string\"}},\"required\":[\"status\"],\"title\":\"CredentialState\",\"type\":\"object\"},\"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\"],\"minLength\":16,\"pattern\":\"^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$\",\"title\":\"DigestMultibase\",\"type\":\"string\"},\"Ext\":{\"additionalProperties\":true,\"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.\",\"minProperties\":1,\"propertyNames\":{\"pattern\":\"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"},\"title\":\"Ext\",\"type\":\"object\"},\"ExternalAccountContextId\":{\"description\":\"The context at the community's VTA that owns an external account. The VTA names an account by its context and its identifier, and decides who may use it by act scope in that context: the community must hold `external-auth-use` there, and be bound to the account, for the account to issue it anything. Naming a context grants nothing; the VTA's binding is the gate.\",\"maxLength\":256,\"minLength\":1,\"title\":\"ExternalAccountContextId\",\"type\":\"string\"},\"ExternalAccountId\":{\"description\":\"The identifier of an external account held by the community's VTA (the external/accounts/* family), which issues the community short-lived, room-scoped credentials and holds the long-lived authority itself.\",\"maxLength\":64,\"minLength\":1,\"pattern\":\"^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$\",\"title\":\"ExternalAccountId\",\"type\":\"string\"},\"Response\":{\"$anchor\":\"response\",\"additionalProperties\":false,\"properties\":{\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"credentialState\":{\"$ref\":\"#/$defs/CredentialState\"},\"ext\":{\"$ref\":\"#/$defs/Ext\"},\"probeOk\":{\"description\":\"Present when the community probed the config with the new credential.\",\"type\":\"boolean\"}},\"required\":[\"configId\",\"credentialState\"],\"title\":\"VTC Storage — Credentials — Set — response payload\",\"type\":\"object\"},\"SealedCredentialCleartext\":{\"$ref\":\"#/$defs/SealedStorageCredential\",\"description\":\"Not a member of the payload: the cleartext inside `sealed`, declared here so that every generated binding carries the type a producer seals and a community opens. Never appears on the wire outside a seal.\"},\"SealedStorageCredential\":{\"description\":\"The cleartext inside the sealed block of vtc/storage/credentials/set/0.2, discriminated by `kind`, which must equal the config's kind. It names the config it is for inside the seal, so that a block captured on its way to one config cannot be replayed into another: a community MUST refuse a block whose `configId` differs from the request's. Never appears on the wire outside a seal, and is never returned. A `local` config has no credential.\",\"oneOf\":[{\"additionalProperties\":false,\"description\":\"An S3 access-key pair: an AWS IAM user's key, or an S3-compatible store's (R2, B2, MinIO).\",\"properties\":{\"accessKeyId\":{\"description\":\"The access key's identifier.\",\"maxLength\":128,\"minLength\":1,\"pattern\":\"^[A-Za-z0-9]+$\",\"type\":\"string\"},\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"kind\":{\"const\":\"s3\"},\"secretAccessKey\":{\"description\":\"The secret half.\",\"maxLength\":256,\"minLength\":1,\"pattern\":\"^[!-~]+$\",\"type\":\"string\"},\"sessionToken\":{\"description\":\"Present when the pair is a temporary one that needs it. A temporary pair stops working at its own expiry, after which the config reports failures until another is set; it is accepted for evaluation, never recommended.\",\"maxLength\":8192,\"minLength\":1,\"pattern\":\"^[!-~]+$\",\"type\":\"string\"}},\"required\":[\"kind\",\"configId\",\"accessKeyId\",\"secretAccessKey\"],\"type\":\"object\"},{\"additionalProperties\":false,\"description\":\"A Google Cloud service-account key.\",\"properties\":{\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"kind\":{\"const\":\"gcs\"},\"serviceAccountKey\":{\"additionalProperties\":true,\"description\":\"The service-account key exactly as Google issues it — the JSON key file, as an object, its member names carried verbatim in Google's own snake_case. Members beyond the four required (`project_id`, `client_id`, `token_uri`, `universe_domain` and the rest) are permitted, and a community MAY discard them: it authenticates with `client_email`, `private_key_id` and `private_key` alone, against Google's own token endpoint, and never against a `token_uri` the key file names.\",\"properties\":{\"client_email\":{\"maxLength\":320,\"minLength\":3,\"pattern\":\"^[^@\\\\s]+@[^@\\\\s]+$\",\"type\":\"string\"},\"private_key\":{\"description\":\"The PKCS #8 PEM private key.\",\"maxLength\":8192,\"minLength\":64,\"pattern\":\"^-----BEGIN PRIVATE KEY-----\",\"type\":\"string\"},\"private_key_id\":{\"maxLength\":128,\"minLength\":1,\"pattern\":\"^[A-Za-z0-9]+$\",\"type\":\"string\"},\"type\":{\"const\":\"service_account\"}},\"required\":[\"type\",\"client_email\",\"private_key_id\",\"private_key\"],\"type\":\"object\"}},\"required\":[\"kind\",\"configId\",\"serviceAccountKey\"],\"type\":\"object\"},{\"additionalProperties\":false,\"description\":\"The Sui key that pays for a `walrus` config's storage.\",\"properties\":{\"configId\":{\"$ref\":\"#/$defs/StorageConfigId\"},\"kind\":{\"const\":\"walrus\"},\"suiPrivateKey\":{\"description\":\"The private key in Sui's Bech32 `suiprivkey` encoding (SIP-15), which carries its signature scheme.\",\"maxLength\":256,\"minLength\":20,\"pattern\":\"^suiprivkey1[02-9ac-hj-np-z]+$\",\"type\":\"string\"}},\"required\":[\"kind\",\"configId\",\"suiPrivateKey\"],\"type\":\"object\"}],\"title\":\"SealedStorageCredential\"},\"StorageConfigId\":{\"description\":\"A storage config's identifier, chosen by the administrator who creates it and immutable thereafter: lowercase letters, digits and inner hyphens, at most 64 characters (`eu-s3-primary`, `walrus-main`). Unique within the community. Never reused after a config is retired, because blobs name their config by this value.\",\"maxLength\":64,\"minLength\":1,\"pattern\":\"^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$\",\"title\":\"StorageConfigId\",\"type\":\"string\"}},\"$ref\":\"#/$defs/Response\",\"$schema\":\"https://json-schema.org/draft/2020-12/schema\"}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The earlier `MAJOR.MINOR` of this slug that this version declares it
/// extends (front matter `extends`, SPEC §5.2 "Declared extension"), or
/// `None`. Only the direct declaration; walk a chain with
/// [`crate::extension::extends_for`].
pub const EXTENDS: Option<&str> = None;
/// 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::NOT_FOUND,
error_codes::NOT_SEALED_MODE,
error_codes::CANNOT_OPEN,
error_codes::INVALID_CREDENTIAL,
error_codes::AUTH_NOT_ALLOWED,
];
/// 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 {
/// `vtc/storage/credentials/set:notFound`
///
/// The community has no storage config with this identifier.
///
/// Declared `retryable: false`.
pub const NOT_FOUND: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/storage/credentials/set:notFound",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/storage/credentials/set:notSealedMode`
///
/// The config's `auth.mode` is not `sealed`. Credentials for a `vta-account` config are the VTA's, set there; an `ambient` config has none.
///
/// Declared `retryable: false`.
pub const NOT_SEALED_MODE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/storage/credentials/set:notSealedMode",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/storage/credentials/set:cannotOpen`
///
/// The sealed block is not sealed to this community, is malformed, or its digest does not match `digestMultibase`. Nothing was stored.
///
/// Declared `retryable: false`.
pub const CANNOT_OPEN: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/storage/credentials/set:cannotOpen",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/storage/credentials/set:invalidCredential`
///
/// The block opened, but its cleartext is not a `SealedStorageCredential` for this config — it does not validate, its `kind` is not the config's, or its `configId` is not the request's. Nothing was stored.
///
/// Declared `retryable: false`.
pub const INVALID_CREDENTIAL: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/storage/credentials/set:invalidCredential",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/storage/credentials/set:authNotAllowed`
///
/// The community's host configuration does not permit `sealed` authentication. Nothing was stored; move the config to a permitted mode with vtc/storage/configs/update.
///
/// Declared `retryable: false`.
pub const AUTH_NOT_ALLOWED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/storage/credentials/set:authNotAllowed",
retryable: false,
details_schema: ::core::option::Option::None,
};
}