//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vault/list`. 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())
}
}
}
///`AttachmentRef`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "AttachmentRef",
/// "type": "object",
/// "required": [
/// "id",
/// "name",
/// "sha256",
/// "sizeBytes"
/// ],
/// "properties": {
/// "contentType": {
/// "description": "Optional MIME type hint for the consumer UI (e.g. \"text/plain\", \"application/x-pem-file\").",
/// "type": "string"
/// },
/// "id": {
/// "description": "Opaque maintainer-assigned id for this attachment; used to fetch the blob via a separate mechanism.",
/// "type": "string",
/// "minLength": 1
/// },
/// "name": {
/// "description": "User-supplied filename (e.g. \"recovery-codes.txt\").",
/// "type": "string",
/// "maxLength": 256,
/// "minLength": 1
/// },
/// "sha256": {
/// "description": "Hex-encoded SHA-256 of the encrypted blob bytes (post-encryption). Lets the consumer verify integrity after fetch.",
/// "type": "string",
/// "pattern": "^[0-9a-f]{64}$"
/// },
/// "sizeBytes": {
/// "description": "Size of the encrypted blob in bytes. Maintainers MAY enforce a maximum per attachment and per entry.",
/// "type": "integer",
/// "minimum": 0.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct AttachmentRef {
///Optional MIME type hint for the consumer UI (e.g. "text/plain", "application/x-pem-file").
#[serde(
rename = "contentType",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub content_type: ::std::option::Option<::std::string::String>,
///Opaque maintainer-assigned id for this attachment; used to fetch the blob via a separate mechanism.
pub id: AttachmentRefId,
///User-supplied filename (e.g. "recovery-codes.txt").
pub name: AttachmentRefName,
///Hex-encoded SHA-256 of the encrypted blob bytes (post-encryption). Lets the consumer verify integrity after fetch.
pub sha256: AttachmentRefSha256,
///Size of the encrypted blob in bytes. Maintainers MAY enforce a maximum per attachment and per entry.
#[serde(rename = "sizeBytes")]
pub size_bytes: u64,
}
///Opaque maintainer-assigned id for this attachment; used to fetch the blob via a separate mechanism.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Opaque maintainer-assigned id for this attachment; used to fetch the blob via a separate mechanism.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct AttachmentRefId(::std::string::String);
impl ::std::ops::Deref for AttachmentRefId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<AttachmentRefId> for ::std::string::String {
fn from(value: AttachmentRefId) -> Self {
value.0
}
}
impl ::std::str::FromStr for AttachmentRefId {
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 AttachmentRefId {
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 AttachmentRefId {
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 AttachmentRefId {
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 AttachmentRefId {
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())
})
}
}
///User-supplied filename (e.g. "recovery-codes.txt").
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "User-supplied filename (e.g. \"recovery-codes.txt\").",
/// "type": "string",
/// "maxLength": 256,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct AttachmentRefName(::std::string::String);
impl ::std::ops::Deref for AttachmentRefName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<AttachmentRefName> for ::std::string::String {
fn from(value: AttachmentRefName) -> Self {
value.0
}
}
impl ::std::str::FromStr for AttachmentRefName {
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 AttachmentRefName {
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 AttachmentRefName {
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 AttachmentRefName {
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 AttachmentRefName {
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())
})
}
}
///Hex-encoded SHA-256 of the encrypted blob bytes (post-encryption). Lets the consumer verify integrity after fetch.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Hex-encoded SHA-256 of the encrypted blob bytes (post-encryption). Lets the consumer verify integrity after fetch.",
/// "type": "string",
/// "pattern": "^[0-9a-f]{64}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct AttachmentRefSha256(::std::string::String);
impl ::std::ops::Deref for AttachmentRefSha256 {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<AttachmentRefSha256> for ::std::string::String {
fn from(value: AttachmentRefSha256) -> Self {
value.0
}
}
impl ::std::str::FromStr for AttachmentRefSha256 {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[0-9a-f]{64}$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[0-9a-f]{64}$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for AttachmentRefSha256 {
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 AttachmentRefSha256 {
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 AttachmentRefSha256 {
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 AttachmentRefSha256 {
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())
})
}
}
///Query a vault maintainer for the metadata view of stored entries. All filters are AND-combined. Pagination is opaque-cursor based; the maintainer chooses page size within the caller's bound and its own ceiling. Secret material is NEVER returned by this task — even when the requesting consumer has VaultRead capability for the entry, the secret is only released via vault/release/0.1.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/vault/list/0.1",
/// "title": "Payload",
/// "description": "Query a vault maintainer for the metadata view of stored entries. All filters are AND-combined. Pagination is opaque-cursor based; the maintainer chooses page size within the caller's bound and its own ceiling. Secret material is NEVER returned by this task — even when the requesting consumer has VaultRead capability for the entry, the secret is only released via vault/release/0.1.",
/// "type": "object",
/// "properties": {
/// "breached": {
/// "description": "Optional filter — when true, only entries with `breachedAt` set are returned; when false, only entries with `breachedAt` absent are returned.",
/// "type": "boolean"
/// },
/// "contextId": {
/// "description": "Optional filter — only entries belonging to this trust context (persona) are returned. Omit to query across all contexts the requesting consumer has VaultRead on.",
/// "type": "string",
/// "minLength": 1
/// },
/// "cursor": {
/// "description": "Opaque continuation token returned by the maintainer in a previous response. Consumers MUST treat the cursor as opaque and re-send it verbatim.",
/// "type": "string"
/// },
/// "expiresBefore": {
/// "description": "Optional filter — only entries whose `expiresAt` is less than this timestamp are returned. Useful for \"what's about to expire\" panels.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "neverUsed": {
/// "description": "Optional filter — when true, only entries with no `lastUsedAt` are returned. Mutually exclusive with `usedSince`; maintainers MUST reject documents that supply both.",
/// "type": "boolean"
/// },
/// "pageSize": {
/// "description": "Maximum number of entries to return. Maintainer-defined default and ceiling; the maintainer MAY return fewer.",
/// "type": "integer",
/// "maximum": 1000.0,
/// "minimum": 1.0
/// },
/// "secretKind": {
/// "description": "Optional filter — only entries of this secret kind are returned.",
/// "$ref": "#/definitions/SecretKind"
/// },
/// "tag": {
/// "description": "Optional filter — only entries whose `tags` array contains this tag.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "targetAndroidPackage": {
/// "description": "Optional filter — only entries with at least one `targets[]` entry of kind `android-app` whose `packageName` exactly equals this value. The typical Android Companion lookup.",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Za-z][A-Za-z0-9_]*(\\.[A-Za-z][A-Za-z0-9_]*)+$"
/// },
/// "targetDid": {
/// "description": "Optional filter — only entries with at least one `targets[]` entry of kind `did` exactly equal to this value.",
/// "type": "string",
/// "minLength": 1
/// },
/// "targetIosBundleId": {
/// "description": "Optional filter — only entries with at least one `targets[]` entry of kind `ios-app` whose `bundleId` exactly equals this value. The typical iOS Companion lookup.",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Za-z0-9.-]+$"
/// },
/// "targetOriginPrefix": {
/// "description": "Optional filter — only entries with at least one `targets[]` entry of kind `web-origin` whose origin starts with this prefix. Useful for \"all my github.* logins\". Maintainers MUST canonicalise (lowercase host) before comparison.",
/// "type": "string",
/// "format": "uri",
/// "minLength": 1
/// },
/// "usedSince": {
/// "description": "Optional filter — only entries whose `lastUsedAt` is greater than or equal to this timestamp are returned. Entries that have never been used are excluded when this filter is present.",
/// "type": "string",
/// "format": "date-time"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Payload {
///Optional filter — when true, only entries with `breachedAt` set are returned; when false, only entries with `breachedAt` absent are returned.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub breached: ::std::option::Option<bool>,
///Optional filter — only entries belonging to this trust context (persona) are returned. Omit to query across all contexts the requesting consumer has VaultRead on.
#[serde(
rename = "contextId",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub context_id: ::std::option::Option<PayloadContextId>,
///Opaque continuation token returned by the maintainer in a previous response. Consumers MUST treat the cursor as opaque and re-send it verbatim.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub cursor: ::std::option::Option<::std::string::String>,
///Optional filter — only entries whose `expiresAt` is less than this timestamp are returned. Useful for "what's about to expire" panels.
#[serde(
rename = "expiresBefore",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub expires_before: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Optional filter — when true, only entries with no `lastUsedAt` are returned. Mutually exclusive with `usedSince`; maintainers MUST reject documents that supply both.
#[serde(
rename = "neverUsed",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub never_used: ::std::option::Option<bool>,
///Maximum number of entries to return. Maintainer-defined default and ceiling; the maintainer MAY return fewer.
#[serde(
rename = "pageSize",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub page_size: ::std::option::Option<::std::num::NonZeroU64>,
///Optional filter — only entries of this secret kind are returned.
#[serde(
rename = "secretKind",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub secret_kind: ::std::option::Option<SecretKind>,
///Optional filter — only entries whose `tags` array contains this tag.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub tag: ::std::option::Option<PayloadTag>,
///Optional filter — only entries with at least one `targets[]` entry of kind `android-app` whose `packageName` exactly equals this value. The typical Android Companion lookup.
#[serde(
rename = "targetAndroidPackage",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub target_android_package: ::std::option::Option<PayloadTargetAndroidPackage>,
///Optional filter — only entries with at least one `targets[]` entry of kind `did` exactly equal to this value.
#[serde(
rename = "targetDid",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub target_did: ::std::option::Option<PayloadTargetDid>,
///Optional filter — only entries with at least one `targets[]` entry of kind `ios-app` whose `bundleId` exactly equals this value. The typical iOS Companion lookup.
#[serde(
rename = "targetIosBundleId",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub target_ios_bundle_id: ::std::option::Option<PayloadTargetIosBundleId>,
///Optional filter — only entries with at least one `targets[]` entry of kind `web-origin` whose origin starts with this prefix. Useful for "all my github.* logins". Maintainers MUST canonicalise (lowercase host) before comparison.
#[serde(
rename = "targetOriginPrefix",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub target_origin_prefix: ::std::option::Option<::std::string::String>,
///Optional filter — only entries whose `lastUsedAt` is greater than or equal to this timestamp are returned. Entries that have never been used are excluded when this filter is present.
#[serde(
rename = "usedSince",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub used_since: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
breached: Default::default(),
context_id: Default::default(),
cursor: Default::default(),
expires_before: Default::default(),
ext: Default::default(),
never_used: Default::default(),
page_size: Default::default(),
secret_kind: Default::default(),
tag: Default::default(),
target_android_package: Default::default(),
target_did: Default::default(),
target_ios_bundle_id: Default::default(),
target_origin_prefix: Default::default(),
used_since: Default::default(),
}
}
}
///Optional filter — only entries belonging to this trust context (persona) are returned. Omit to query across all contexts the requesting consumer has VaultRead on.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional filter — only entries belonging to this trust context (persona) are returned. Omit to query across all contexts the requesting consumer has VaultRead on.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadContextId(::std::string::String);
impl ::std::ops::Deref for PayloadContextId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadContextId> for ::std::string::String {
fn from(value: PayloadContextId) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadContextId {
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 PayloadContextId {
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 PayloadContextId {
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 PayloadContextId {
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 PayloadContextId {
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())
})
}
}
///Optional filter — only entries whose `tags` array contains this tag.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional filter — only entries whose `tags` array contains this tag.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadTag(::std::string::String);
impl ::std::ops::Deref for PayloadTag {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadTag> for ::std::string::String {
fn from(value: PayloadTag) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadTag {
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());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadTag {
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 PayloadTag {
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 PayloadTag {
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 PayloadTag {
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())
})
}
}
///Optional filter — only entries with at least one `targets[]` entry of kind `android-app` whose `packageName` exactly equals this value. The typical Android Companion lookup.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional filter — only entries with at least one `targets[]` entry of kind `android-app` whose `packageName` exactly equals this value. The typical Android Companion lookup.",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Za-z][A-Za-z0-9_]*(\\.[A-Za-z][A-Za-z0-9_]*)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadTargetAndroidPackage(::std::string::String);
impl ::std::ops::Deref for PayloadTargetAndroidPackage {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadTargetAndroidPackage> for ::std::string::String {
fn from(value: PayloadTargetAndroidPackage) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadTargetAndroidPackage {
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());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[A-Za-z][A-Za-z0-9_]*(\\.[A-Za-z][A-Za-z0-9_]*)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[A-Za-z][A-Za-z0-9_]*(\\.[A-Za-z][A-Za-z0-9_]*)+$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadTargetAndroidPackage {
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 PayloadTargetAndroidPackage {
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 PayloadTargetAndroidPackage {
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 PayloadTargetAndroidPackage {
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())
})
}
}
///Optional filter — only entries with at least one `targets[]` entry of kind `did` exactly equal to this value.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional filter — only entries with at least one `targets[]` entry of kind `did` exactly equal to this value.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadTargetDid(::std::string::String);
impl ::std::ops::Deref for PayloadTargetDid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadTargetDid> for ::std::string::String {
fn from(value: PayloadTargetDid) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadTargetDid {
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 PayloadTargetDid {
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 PayloadTargetDid {
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 PayloadTargetDid {
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 PayloadTargetDid {
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())
})
}
}
///Optional filter — only entries with at least one `targets[]` entry of kind `ios-app` whose `bundleId` exactly equals this value. The typical iOS Companion lookup.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional filter — only entries with at least one `targets[]` entry of kind `ios-app` whose `bundleId` exactly equals this value. The typical iOS Companion lookup.",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Za-z0-9.-]+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadTargetIosBundleId(::std::string::String);
impl ::std::ops::Deref for PayloadTargetIosBundleId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadTargetIosBundleId> for ::std::string::String {
fn from(value: PayloadTargetIosBundleId) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadTargetIosBundleId {
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());
}
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 PayloadTargetIosBundleId {
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 PayloadTargetIosBundleId {
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 PayloadTargetIosBundleId {
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 PayloadTargetIosBundleId {
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 success response to a vault/list request. Carried in a Trust Task document whose type is https://trusttasks.org/spec/vault/list/0.1#response.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "The success response to a vault/list request. Carried in a Trust Task document whose type is https://trusttasks.org/spec/vault/list/0.1#response.",
/// "type": "object",
/// "required": [
/// "entries",
/// "truncated"
/// ],
/// "properties": {
/// "cursor": {
/// "description": "Opaque continuation token to fetch the next page. Present only when `truncated` is true AND the maintainer supports pagination from this point.",
/// "type": "string"
/// },
/// "entries": {
/// "description": "Matching VaultEntry items in metadata-only view, in maintainer-defined order (RECOMMENDED: most-recently-used first when no other order is implied by the filters). May be empty.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/VaultEntry"
/// }
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "redactedFields": {
/// "description": "Names of VaultEntry fields the maintainer redacted from every returned entry (for example, ['lastUsedAt'] when releasing to a consumer whose policy does not permit timing-data exposure).",
/// "type": "array",
/// "items": {
/// "type": "string"
/// }
/// },
/// "truncated": {
/// "description": "true when more matching entries exist beyond `entries`; false when this response is the complete result. Independent of `cursor`: a maintainer MAY truncate without supporting pagination, in which case `truncated` is true and `cursor` is absent.",
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Response {
///Opaque continuation token to fetch the next page. Present only when `truncated` is true AND the maintainer supports pagination from this point.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub cursor: ::std::option::Option<::std::string::String>,
///Matching VaultEntry items in metadata-only view, in maintainer-defined order (RECOMMENDED: most-recently-used first when no other order is implied by the filters). May be empty.
pub entries: ::std::vec::Vec<VaultEntry>,
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Names of VaultEntry fields the maintainer redacted from every returned entry (for example, ['lastUsedAt'] when releasing to a consumer whose policy does not permit timing-data exposure).
#[serde(
rename = "redactedFields",
default,
skip_serializing_if = "::std::vec::Vec::is_empty"
)]
pub redacted_fields: ::std::vec::Vec<::std::string::String>,
///true when more matching entries exist beyond `entries`; false when this response is the complete result. Independent of `cursor`: a maintainer MAY truncate without supporting pagination, in which case `truncated` is true and `cursor` is absent.
pub truncated: bool,
}
/**Discriminator for the secret type stored in the entry. Definitions:
- `password` — username + password (+ optional TOTP seed).
- `passkey` — WebAuthn discoverable credential (private key + rpId + userHandle).
- `oauth-tokens` — OAuth 2.0 refresh + access token bundle for a specific provider.
- `did-self-issued` — Self-Issued OpenID Provider v2 (SIOP) credential: the entry points at a DID + signing key already managed by the VTA.
- `didcomm-peer` — DIDComm peer identity used to authenticate against a DIDComm-speaking relying party.
- `bearer-token` — opaque bearer token carried in a maintainer-named header (covers API tokens, long-lived JWTs, personal-access tokens).
- `ssh-key` — SSH private key + comment.
- `custom` — arbitrary structured fields; release-time consumer responsible for interpretation.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "SecretKind",
/// "description": "Discriminator for the secret type stored in the entry. Definitions:\n- `password` — username + password (+ optional TOTP seed).\n- `passkey` — WebAuthn discoverable credential (private key + rpId + userHandle).\n- `oauth-tokens` — OAuth 2.0 refresh + access token bundle for a specific provider.\n- `did-self-issued` — Self-Issued OpenID Provider v2 (SIOP) credential: the entry points at a DID + signing key already managed by the VTA.\n- `didcomm-peer` — DIDComm peer identity used to authenticate against a DIDComm-speaking relying party.\n- `bearer-token` — opaque bearer token carried in a maintainer-named header (covers API tokens, long-lived JWTs, personal-access tokens).\n- `ssh-key` — SSH private key + comment.\n- `custom` — arbitrary structured fields; release-time consumer responsible for interpretation.",
/// "type": "string",
/// "enum": [
/// "password",
/// "passkey",
/// "oauth-tokens",
/// "did-self-issued",
/// "didcomm-peer",
/// "bearer-token",
/// "ssh-key",
/// "custom"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum SecretKind {
#[serde(rename = "password")]
Password,
#[serde(rename = "passkey")]
Passkey,
#[serde(rename = "oauth-tokens")]
OauthTokens,
#[serde(rename = "did-self-issued")]
DidSelfIssued,
#[serde(rename = "didcomm-peer")]
DidcommPeer,
#[serde(rename = "bearer-token")]
BearerToken,
#[serde(rename = "ssh-key")]
SshKey,
#[serde(rename = "custom")]
Custom,
}
impl ::std::fmt::Display for SecretKind {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Password => f.write_str("password"),
Self::Passkey => f.write_str("passkey"),
Self::OauthTokens => f.write_str("oauth-tokens"),
Self::DidSelfIssued => f.write_str("did-self-issued"),
Self::DidcommPeer => f.write_str("didcomm-peer"),
Self::BearerToken => f.write_str("bearer-token"),
Self::SshKey => f.write_str("ssh-key"),
Self::Custom => f.write_str("custom"),
}
}
}
impl ::std::str::FromStr for SecretKind {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"password" => Ok(Self::Password),
"passkey" => Ok(Self::Passkey),
"oauth-tokens" => Ok(Self::OauthTokens),
"did-self-issued" => Ok(Self::DidSelfIssued),
"didcomm-peer" => Ok(Self::DidcommPeer),
"bearer-token" => Ok(Self::BearerToken),
"ssh-key" => Ok(Self::SshKey),
"custom" => Ok(Self::Custom),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SecretKind {
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 SecretKind {
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 SecretKind {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///A single binding target for a vault entry. Tagged union over the discriminator `kind`. A VaultEntry's `targets` array MAY mix any number of these.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "SiteTarget",
/// "description": "A single binding target for a vault entry. Tagged union over the discriminator `kind`. A VaultEntry's `targets` array MAY mix any number of these.",
/// "oneOf": [
/// {
/// "title": "WebOrigin",
/// "type": "object",
/// "required": [
/// "kind",
/// "origin"
/// ],
/// "properties": {
/// "kind": {
/// "const": "web-origin"
/// },
/// "origin": {
/// "description": "Web origin per RFC 6454 (scheme + host + optional port), e.g. \"https://github.com\". Compared by exact string equality after canonicalisation (lowercase host, default port elided). Consumers wanting subdomain coverage SHOULD add multiple targets, not encode a wildcard.",
/// "type": "string",
/// "format": "uri"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "Did",
/// "type": "object",
/// "required": [
/// "did",
/// "kind"
/// ],
/// "properties": {
/// "did": {
/// "description": "DID identifying the relying party (e.g. did:web:rp.example). The vault maintainer is responsible for any DID resolution required to act on this entry.",
/// "type": "string",
/// "minLength": 1
/// },
/// "kind": {
/// "const": "did"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "IosApp",
/// "type": "object",
/// "required": [
/// "bundleId",
/// "kind"
/// ],
/// "properties": {
/// "bundleId": {
/// "description": "iOS bundle identifier in reverse-DNS form (e.g. \"com.github.stwalkerster.codehub\"). Compared by exact string equality. Matches when an iOS Companion identifies the requesting app via its bundle id (typically via the OS Credential Manager integration).",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Za-z0-9.-]+$"
/// },
/// "kind": {
/// "const": "ios-app"
/// },
/// "teamId": {
/// "description": "Optional Apple Developer Team identifier (10-character alphanumeric). When supplied, the maintainer SHOULD also verify the team id of the requesting app before matching — defense in depth against bundle-id squatting on jailbroken devices.",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Z0-9]+$"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "AndroidApp",
/// "type": "object",
/// "required": [
/// "kind",
/// "packageName",
/// "sha256CertFingerprints"
/// ],
/// "properties": {
/// "kind": {
/// "const": "android-app"
/// },
/// "packageName": {
/// "description": "Android package name in reverse-DNS form (e.g. \"com.github.android\").",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Za-z][A-Za-z0-9_]*(\\.[A-Za-z][A-Za-z0-9_]*)+$"
/// },
/// "sha256CertFingerprints": {
/// "description": "SHA-256 fingerprints of the app's signing certificates, in colon-separated hex (the format `apksigner` and the Play Console emit). At least one fingerprint MUST be present. The maintainer matches when ANY of the provided fingerprints matches the requesting app's signature — this supports apps signed by multiple keys (e.g. during certificate rotation via Play App Signing).",
/// "type": "array",
/// "items": {
/// "type": "string",
/// "pattern": "^[0-9A-F]{2}(:[0-9A-F]{2}){31}$"
/// },
/// "minItems": 1,
/// "uniqueItems": true
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(tag = "kind", deny_unknown_fields)]
pub enum SiteTarget {
///WebOrigin
#[serde(rename = "web-origin")]
WebOrigin {
///Web origin per RFC 6454 (scheme + host + optional port), e.g. "https://github.com". Compared by exact string equality after canonicalisation (lowercase host, default port elided). Consumers wanting subdomain coverage SHOULD add multiple targets, not encode a wildcard.
origin: ::std::string::String,
},
///Did
#[serde(rename = "did")]
Did {
///DID identifying the relying party (e.g. did:web:rp.example). The vault maintainer is responsible for any DID resolution required to act on this entry.
did: SiteTargetDid,
},
///IosApp
#[serde(rename = "ios-app")]
IosApp {
///iOS bundle identifier in reverse-DNS form (e.g. "com.github.stwalkerster.codehub"). Compared by exact string equality. Matches when an iOS Companion identifies the requesting app via its bundle id (typically via the OS Credential Manager integration).
#[serde(rename = "bundleId")]
bundle_id: SiteTargetBundleId,
///Optional Apple Developer Team identifier (10-character alphanumeric). When supplied, the maintainer SHOULD also verify the team id of the requesting app before matching — defense in depth against bundle-id squatting on jailbroken devices.
#[serde(
rename = "teamId",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
team_id: ::std::option::Option<SiteTargetTeamId>,
},
///AndroidApp
#[serde(rename = "android-app")]
AndroidApp {
///Android package name in reverse-DNS form (e.g. "com.github.android").
#[serde(rename = "packageName")]
package_name: SiteTargetPackageName,
///SHA-256 fingerprints of the app's signing certificates, in colon-separated hex (the format `apksigner` and the Play Console emit). At least one fingerprint MUST be present. The maintainer matches when ANY of the provided fingerprints matches the requesting app's signature — this supports apps signed by multiple keys (e.g. during certificate rotation via Play App Signing).
#[serde(rename = "sha256CertFingerprints")]
sha256_cert_fingerprints: Vec<SiteTargetSha256CertFingerprintsItem>,
},
}
///iOS bundle identifier in reverse-DNS form (e.g. "com.github.stwalkerster.codehub"). Compared by exact string equality. Matches when an iOS Companion identifies the requesting app via its bundle id (typically via the OS Credential Manager integration).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "iOS bundle identifier in reverse-DNS form (e.g. \"com.github.stwalkerster.codehub\"). Compared by exact string equality. Matches when an iOS Companion identifies the requesting app via its bundle id (typically via the OS Credential Manager integration).",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Za-z0-9.-]+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SiteTargetBundleId(::std::string::String);
impl ::std::ops::Deref for SiteTargetBundleId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SiteTargetBundleId> for ::std::string::String {
fn from(value: SiteTargetBundleId) -> Self {
value.0
}
}
impl ::std::str::FromStr for SiteTargetBundleId {
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());
}
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 SiteTargetBundleId {
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 SiteTargetBundleId {
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 SiteTargetBundleId {
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 SiteTargetBundleId {
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())
})
}
}
///DID identifying the relying party (e.g. did:web:rp.example). The vault maintainer is responsible for any DID resolution required to act on this entry.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "DID identifying the relying party (e.g. did:web:rp.example). The vault maintainer is responsible for any DID resolution required to act on this entry.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SiteTargetDid(::std::string::String);
impl ::std::ops::Deref for SiteTargetDid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SiteTargetDid> for ::std::string::String {
fn from(value: SiteTargetDid) -> Self {
value.0
}
}
impl ::std::str::FromStr for SiteTargetDid {
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 SiteTargetDid {
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 SiteTargetDid {
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 SiteTargetDid {
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 SiteTargetDid {
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())
})
}
}
///Android package name in reverse-DNS form (e.g. "com.github.android").
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Android package name in reverse-DNS form (e.g. \"com.github.android\").",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Za-z][A-Za-z0-9_]*(\\.[A-Za-z][A-Za-z0-9_]*)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SiteTargetPackageName(::std::string::String);
impl ::std::ops::Deref for SiteTargetPackageName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SiteTargetPackageName> for ::std::string::String {
fn from(value: SiteTargetPackageName) -> Self {
value.0
}
}
impl ::std::str::FromStr for SiteTargetPackageName {
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());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[A-Za-z][A-Za-z0-9_]*(\\.[A-Za-z][A-Za-z0-9_]*)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[A-Za-z][A-Za-z0-9_]*(\\.[A-Za-z][A-Za-z0-9_]*)+$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SiteTargetPackageName {
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 SiteTargetPackageName {
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 SiteTargetPackageName {
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 SiteTargetPackageName {
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())
})
}
}
///`SiteTargetSha256CertFingerprintsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[0-9A-F]{2}(:[0-9A-F]{2}){31}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SiteTargetSha256CertFingerprintsItem(::std::string::String);
impl ::std::ops::Deref for SiteTargetSha256CertFingerprintsItem {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SiteTargetSha256CertFingerprintsItem> for ::std::string::String {
fn from(value: SiteTargetSha256CertFingerprintsItem) -> Self {
value.0
}
}
impl ::std::str::FromStr for SiteTargetSha256CertFingerprintsItem {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[0-9A-F]{2}(:[0-9A-F]{2}){31}$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[0-9A-F]{2}(:[0-9A-F]{2}){31}$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SiteTargetSha256CertFingerprintsItem {
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 SiteTargetSha256CertFingerprintsItem {
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 SiteTargetSha256CertFingerprintsItem {
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 SiteTargetSha256CertFingerprintsItem {
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())
})
}
}
///Optional Apple Developer Team identifier (10-character alphanumeric). When supplied, the maintainer SHOULD also verify the team id of the requesting app before matching — defense in depth against bundle-id squatting on jailbroken devices.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional Apple Developer Team identifier (10-character alphanumeric). When supplied, the maintainer SHOULD also verify the team id of the requesting app before matching — defense in depth against bundle-id squatting on jailbroken devices.",
/// "type": "string",
/// "minLength": 1,
/// "pattern": "^[A-Z0-9]+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SiteTargetTeamId(::std::string::String);
impl ::std::ops::Deref for SiteTargetTeamId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SiteTargetTeamId> for ::std::string::String {
fn from(value: SiteTargetTeamId) -> Self {
value.0
}
}
impl ::std::str::FromStr for SiteTargetTeamId {
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());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[A-Z0-9]+$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[A-Z0-9]+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SiteTargetTeamId {
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 SiteTargetTeamId {
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 SiteTargetTeamId {
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 SiteTargetTeamId {
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())
})
}
}
///`VaultEntry`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "VaultEntry",
/// "type": "object",
/// "required": [
/// "contextId",
/// "createdAt",
/// "id",
/// "label",
/// "secretKind",
/// "targets",
/// "updatedAt",
/// "version"
/// ],
/// "properties": {
/// "attachments": {
/// "description": "References to encrypted blobs associated with the entry (recovery codes, PEM files, screenshots of authenticator setup). The blobs themselves are fetched via a separate mechanism the maintainer documents; metadata view exposes only the descriptor.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/AttachmentRef"
/// }
/// },
/// "breachedAt": {
/// "description": "Set by the maintainer (via HIBP integration or equivalent) when the password material associated with this entry is known to appear in a public breach. Consumers SHOULD surface this prominently. Cleared when the user rotates the password and the new password is not in any known breach.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "contextId": {
/// "description": "Identifier of the trust context (persona) the entry belongs to. Opaque string interpreted by the vault maintainer; corresponds to a single ContextRecord on the VTA side.",
/// "type": "string",
/// "minLength": 1
/// },
/// "createdAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "createdBy": {
/// "description": "VID of the consumer that originally created the entry.",
/// "type": "string"
/// },
/// "customFieldNames": {
/// "description": "Names of additional fields the user has attached (e.g. [\"security-question-1\", \"account-number\"]). The VALUES live in the secret payload and are only delivered by vault/release/0.1. Exposing names in metadata lets the consumer render the right form layout before requesting release.",
/// "type": "array",
/// "items": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "uniqueItems": true
/// },
/// "expiresAt": {
/// "description": "Optional time after which the credential is no longer expected to be valid (e.g. an OAuth refresh token's known expiry, a time-limited API token, an enterprise password rotation policy). Maintainers MAY surface this in the consumer UI as a warning.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1. Reverse-DNS-namespaced; consumers MUST ignore unrecognized namespaces.",
/// "$ref": "#/definitions/Ext"
/// },
/// "favicon": {
/// "description": "Optional URI of an icon to display in the consumer UI. Maintainers MAY fetch and cache; consumers SHOULD treat as untrusted content and fetch via a sandboxed pipeline.",
/// "type": "string",
/// "format": "uri"
/// },
/// "id": {
/// "description": "Opaque vault-maintainer-assigned identifier for the entry. ULID/UUID/base32 are common; the wire spec only requires non-empty string equality.",
/// "type": "string",
/// "minLength": 1
/// },
/// "label": {
/// "description": "Human-readable display name (e.g. \"Work GitHub\", \"Personal bank — checking\"). Maintainers MAY enforce a maximum length; the wire spec does not.",
/// "type": "string",
/// "minLength": 1
/// },
/// "lastUsedAt": {
/// "description": "Most recent time the entry was used (either released or proxy-login performed). Maintainers MAY return this with reduced precision (e.g. hour-floored) when releasing to a less-trusted consumer.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "notes": {
/// "description": "Non-sensitive notes the user attached to the entry. Visible in metadata view (suitable for support contact, account number, expiry policy memos). SENSITIVE notes belong in the secret payload as a `secureNotes` field — those are only released by vault/release/0.1.",
/// "type": "string",
/// "maxLength": 4096
/// },
/// "passwordChangedAt": {
/// "description": "Set whenever the password component of the secret payload is rotated. Maintainers MUST update this on every secret-material change for entries of kind `password` (or any kind that carries a password component). Used by consumers to surface rotation-overdue warnings.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "principalDid": {
/// "description": "Optional cached DID the entry will act AS for DID-shaped flows — mirrors the `did` field of the entry's secret payload when `secretKind` carries one (`did-self-issued`, `didcomm-peer`). Absent for kinds that have no DID concept (`password`, `passkey`, `oauth-tokens`, `bearer-token`, `ssh-key`, `custom`). MAINTAINER-DERIVED, NOT CONSUMER-SUPPLIED: the maintainer MUST recompute this from the canonical secret at every upsert / secret rotation; a producer-supplied value on `vault/upsert/0.1` MUST be ignored (no error, but no honour). Read-only on the wire, present in metadata views so consumers can drive RP-side flows (e.g. fetch `/auth/challenge` keyed on the principal DID before requesting a proxy-login) without releasing the secret.",
/// "type": "string",
/// "minLength": 1
/// },
/// "secretKind": {
/// "description": "Discriminator for the kind of secret this entry holds. The secret material itself is NEVER returned in metadata views; the kind is exposed so consumers can render an appropriate UI affordance and so policy decisions can route by kind.",
/// "$ref": "#/definitions/SecretKind"
/// },
/// "selectors": {
/// "description": "Opaque maintainer-defined selector strings fed to the policy engine when this entry is requested (e.g. \"recent_uv_required\", \"network_class=corp\", \"step_up_push\"). Consumers MUST treat selectors as opaque; they exist for policy authoring on the maintainer side.",
/// "type": "array",
/// "items": {
/// "type": "string",
/// "minLength": 1
/// },
/// "uniqueItems": true
/// },
/// "tags": {
/// "description": "User-defined tags for organisation and filtering (e.g. [\"family\", \"finance\"]). Maintainers MAY enforce a maximum count; the wire spec does not.",
/// "type": "array",
/// "items": {
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "uniqueItems": true
/// },
/// "targets": {
/// "description": "One or more binding targets — web origins, mobile app identifiers, and/or DIDs — that this credential applies to. A request from any matching target uses this entry. A typical entry for a service that exists as both a website and mobile apps will list a web origin, an iOS bundle id, and an Android package id; passkeys for that service typically list only the origin (because iOS Associated Domains and Android Asset Links bind apps to the domain at the OS level).",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/SiteTarget"
/// },
/// "minItems": 1
/// },
/// "updatedAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "updatedBy": {
/// "description": "VID of the consumer that last modified the entry.",
/// "type": "string"
/// },
/// "version": {
/// "description": "Monotonic version counter incremented on every mutation. Used by consumers for optimistic-concurrency checks on vault/upsert and as the seq baseline for vault/sync.",
/// "type": "integer",
/// "minimum": 0.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct VaultEntry {
///References to encrypted blobs associated with the entry (recovery codes, PEM files, screenshots of authenticator setup). The blobs themselves are fetched via a separate mechanism the maintainer documents; metadata view exposes only the descriptor.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub attachments: ::std::vec::Vec<AttachmentRef>,
///Set by the maintainer (via HIBP integration or equivalent) when the password material associated with this entry is known to appear in a public breach. Consumers SHOULD surface this prominently. Cleared when the user rotates the password and the new password is not in any known breach.
#[serde(
rename = "breachedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub breached_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Identifier of the trust context (persona) the entry belongs to. Opaque string interpreted by the vault maintainer; corresponds to a single ContextRecord on the VTA side.
#[serde(rename = "contextId")]
pub context_id: VaultEntryContextId,
#[serde(rename = "createdAt")]
pub created_at: ::chrono::DateTime<::chrono::offset::Utc>,
///VID of the consumer that originally created the entry.
#[serde(
rename = "createdBy",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub created_by: ::std::option::Option<::std::string::String>,
///Names of additional fields the user has attached (e.g. ["security-question-1", "account-number"]). The VALUES live in the secret payload and are only delivered by vault/release/0.1. Exposing names in metadata lets the consumer render the right form layout before requesting release.
#[serde(
rename = "customFieldNames",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub custom_field_names: ::std::option::Option<Vec<VaultEntryCustomFieldNamesItem>>,
///Optional time after which the credential is no longer expected to be valid (e.g. an OAuth refresh token's known expiry, a time-limited API token, an enterprise password rotation policy). Maintainers MAY surface this in the consumer UI as a warning.
#[serde(
rename = "expiresAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub expires_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Ecosystem-defined extension members per SPEC.md §4.5.1. Reverse-DNS-namespaced; consumers MUST ignore unrecognized namespaces.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Optional URI of an icon to display in the consumer UI. Maintainers MAY fetch and cache; consumers SHOULD treat as untrusted content and fetch via a sandboxed pipeline.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub favicon: ::std::option::Option<::std::string::String>,
///Opaque vault-maintainer-assigned identifier for the entry. ULID/UUID/base32 are common; the wire spec only requires non-empty string equality.
pub id: VaultEntryId,
///Human-readable display name (e.g. "Work GitHub", "Personal bank — checking"). Maintainers MAY enforce a maximum length; the wire spec does not.
pub label: VaultEntryLabel,
///Most recent time the entry was used (either released or proxy-login performed). Maintainers MAY return this with reduced precision (e.g. hour-floored) when releasing to a less-trusted consumer.
#[serde(
rename = "lastUsedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub last_used_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Non-sensitive notes the user attached to the entry. Visible in metadata view (suitable for support contact, account number, expiry policy memos). SENSITIVE notes belong in the secret payload as a `secureNotes` field — those are only released by vault/release/0.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub notes: ::std::option::Option<VaultEntryNotes>,
///Set whenever the password component of the secret payload is rotated. Maintainers MUST update this on every secret-material change for entries of kind `password` (or any kind that carries a password component). Used by consumers to surface rotation-overdue warnings.
#[serde(
rename = "passwordChangedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub password_changed_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Optional cached DID the entry will act AS for DID-shaped flows — mirrors the `did` field of the entry's secret payload when `secretKind` carries one (`did-self-issued`, `didcomm-peer`). Absent for kinds that have no DID concept (`password`, `passkey`, `oauth-tokens`, `bearer-token`, `ssh-key`, `custom`). MAINTAINER-DERIVED, NOT CONSUMER-SUPPLIED: the maintainer MUST recompute this from the canonical secret at every upsert / secret rotation; a producer-supplied value on `vault/upsert/0.1` MUST be ignored (no error, but no honour). Read-only on the wire, present in metadata views so consumers can drive RP-side flows (e.g. fetch `/auth/challenge` keyed on the principal DID before requesting a proxy-login) without releasing the secret.
#[serde(
rename = "principalDid",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub principal_did: ::std::option::Option<VaultEntryPrincipalDid>,
///Discriminator for the kind of secret this entry holds. The secret material itself is NEVER returned in metadata views; the kind is exposed so consumers can render an appropriate UI affordance and so policy decisions can route by kind.
#[serde(rename = "secretKind")]
pub secret_kind: SecretKind,
///Opaque maintainer-defined selector strings fed to the policy engine when this entry is requested (e.g. "recent_uv_required", "network_class=corp", "step_up_push"). Consumers MUST treat selectors as opaque; they exist for policy authoring on the maintainer side.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub selectors: ::std::option::Option<Vec<VaultEntrySelectorsItem>>,
///User-defined tags for organisation and filtering (e.g. ["family", "finance"]). Maintainers MAY enforce a maximum count; the wire spec does not.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub tags: ::std::option::Option<Vec<VaultEntryTagsItem>>,
///One or more binding targets — web origins, mobile app identifiers, and/or DIDs — that this credential applies to. A request from any matching target uses this entry. A typical entry for a service that exists as both a website and mobile apps will list a web origin, an iOS bundle id, and an Android package id; passkeys for that service typically list only the origin (because iOS Associated Domains and Android Asset Links bind apps to the domain at the OS level).
pub targets: ::std::vec::Vec<SiteTarget>,
#[serde(rename = "updatedAt")]
pub updated_at: ::chrono::DateTime<::chrono::offset::Utc>,
///VID of the consumer that last modified the entry.
#[serde(
rename = "updatedBy",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub updated_by: ::std::option::Option<::std::string::String>,
///Monotonic version counter incremented on every mutation. Used by consumers for optimistic-concurrency checks on vault/upsert and as the seq baseline for vault/sync.
pub version: u64,
}
///Identifier of the trust context (persona) the entry belongs to. Opaque string interpreted by the vault maintainer; corresponds to a single ContextRecord on the VTA side.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Identifier of the trust context (persona) the entry belongs to. Opaque string interpreted by the vault maintainer; corresponds to a single ContextRecord on the VTA side.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VaultEntryContextId(::std::string::String);
impl ::std::ops::Deref for VaultEntryContextId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VaultEntryContextId> for ::std::string::String {
fn from(value: VaultEntryContextId) -> Self {
value.0
}
}
impl ::std::str::FromStr for VaultEntryContextId {
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 VaultEntryContextId {
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 VaultEntryContextId {
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 VaultEntryContextId {
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 VaultEntryContextId {
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())
})
}
}
///`VaultEntryCustomFieldNamesItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VaultEntryCustomFieldNamesItem(::std::string::String);
impl ::std::ops::Deref for VaultEntryCustomFieldNamesItem {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VaultEntryCustomFieldNamesItem> for ::std::string::String {
fn from(value: VaultEntryCustomFieldNamesItem) -> Self {
value.0
}
}
impl ::std::str::FromStr for VaultEntryCustomFieldNamesItem {
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());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VaultEntryCustomFieldNamesItem {
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 VaultEntryCustomFieldNamesItem {
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 VaultEntryCustomFieldNamesItem {
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 VaultEntryCustomFieldNamesItem {
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())
})
}
}
///Opaque vault-maintainer-assigned identifier for the entry. ULID/UUID/base32 are common; the wire spec only requires non-empty string equality.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Opaque vault-maintainer-assigned identifier for the entry. ULID/UUID/base32 are common; the wire spec only requires non-empty string equality.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VaultEntryId(::std::string::String);
impl ::std::ops::Deref for VaultEntryId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VaultEntryId> for ::std::string::String {
fn from(value: VaultEntryId) -> Self {
value.0
}
}
impl ::std::str::FromStr for VaultEntryId {
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 VaultEntryId {
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 VaultEntryId {
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 VaultEntryId {
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 VaultEntryId {
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())
})
}
}
///Human-readable display name (e.g. "Work GitHub", "Personal bank — checking"). Maintainers MAY enforce a maximum length; the wire spec does not.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Human-readable display name (e.g. \"Work GitHub\", \"Personal bank — checking\"). Maintainers MAY enforce a maximum length; the wire spec does not.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VaultEntryLabel(::std::string::String);
impl ::std::ops::Deref for VaultEntryLabel {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VaultEntryLabel> for ::std::string::String {
fn from(value: VaultEntryLabel) -> Self {
value.0
}
}
impl ::std::str::FromStr for VaultEntryLabel {
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 VaultEntryLabel {
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 VaultEntryLabel {
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 VaultEntryLabel {
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 VaultEntryLabel {
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())
})
}
}
///Non-sensitive notes the user attached to the entry. Visible in metadata view (suitable for support contact, account number, expiry policy memos). SENSITIVE notes belong in the secret payload as a `secureNotes` field — those are only released by vault/release/0.1.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Non-sensitive notes the user attached to the entry. Visible in metadata view (suitable for support contact, account number, expiry policy memos). SENSITIVE notes belong in the secret payload as a `secureNotes` field — those are only released by vault/release/0.1.",
/// "type": "string",
/// "maxLength": 4096
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VaultEntryNotes(::std::string::String);
impl ::std::ops::Deref for VaultEntryNotes {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VaultEntryNotes> for ::std::string::String {
fn from(value: VaultEntryNotes) -> Self {
value.0
}
}
impl ::std::str::FromStr for VaultEntryNotes {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 4096usize {
return Err("longer than 4096 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VaultEntryNotes {
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 VaultEntryNotes {
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 VaultEntryNotes {
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 VaultEntryNotes {
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())
})
}
}
///Optional cached DID the entry will act AS for DID-shaped flows — mirrors the `did` field of the entry's secret payload when `secretKind` carries one (`did-self-issued`, `didcomm-peer`). Absent for kinds that have no DID concept (`password`, `passkey`, `oauth-tokens`, `bearer-token`, `ssh-key`, `custom`). MAINTAINER-DERIVED, NOT CONSUMER-SUPPLIED: the maintainer MUST recompute this from the canonical secret at every upsert / secret rotation; a producer-supplied value on `vault/upsert/0.1` MUST be ignored (no error, but no honour). Read-only on the wire, present in metadata views so consumers can drive RP-side flows (e.g. fetch `/auth/challenge` keyed on the principal DID before requesting a proxy-login) without releasing the secret.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional cached DID the entry will act AS for DID-shaped flows — mirrors the `did` field of the entry's secret payload when `secretKind` carries one (`did-self-issued`, `didcomm-peer`). Absent for kinds that have no DID concept (`password`, `passkey`, `oauth-tokens`, `bearer-token`, `ssh-key`, `custom`). MAINTAINER-DERIVED, NOT CONSUMER-SUPPLIED: the maintainer MUST recompute this from the canonical secret at every upsert / secret rotation; a producer-supplied value on `vault/upsert/0.1` MUST be ignored (no error, but no honour). Read-only on the wire, present in metadata views so consumers can drive RP-side flows (e.g. fetch `/auth/challenge` keyed on the principal DID before requesting a proxy-login) without releasing the secret.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VaultEntryPrincipalDid(::std::string::String);
impl ::std::ops::Deref for VaultEntryPrincipalDid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VaultEntryPrincipalDid> for ::std::string::String {
fn from(value: VaultEntryPrincipalDid) -> Self {
value.0
}
}
impl ::std::str::FromStr for VaultEntryPrincipalDid {
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 VaultEntryPrincipalDid {
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 VaultEntryPrincipalDid {
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 VaultEntryPrincipalDid {
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 VaultEntryPrincipalDid {
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())
})
}
}
///`VaultEntrySelectorsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VaultEntrySelectorsItem(::std::string::String);
impl ::std::ops::Deref for VaultEntrySelectorsItem {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VaultEntrySelectorsItem> for ::std::string::String {
fn from(value: VaultEntrySelectorsItem) -> Self {
value.0
}
}
impl ::std::str::FromStr for VaultEntrySelectorsItem {
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 VaultEntrySelectorsItem {
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 VaultEntrySelectorsItem {
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 VaultEntrySelectorsItem {
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 VaultEntrySelectorsItem {
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())
})
}
}
///`VaultEntryTagsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct VaultEntryTagsItem(::std::string::String);
impl ::std::ops::Deref for VaultEntryTagsItem {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<VaultEntryTagsItem> for ::std::string::String {
fn from(value: VaultEntryTagsItem) -> Self {
value.0
}
}
impl ::std::str::FromStr for VaultEntryTagsItem {
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());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for VaultEntryTagsItem {
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 VaultEntryTagsItem {
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 VaultEntryTagsItem {
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 VaultEntryTagsItem {
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())
})
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vault/list/0.1";
}
impl crate::Payload for Response {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vault/list/0.1#response";
}
#[cfg(feature = "validate")]
impl crate::validate::ValidatedPayload for Payload {
const SCHEMA_JSON: &'static str = "{\n \"$defs\": {\n \"AttachmentRef\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"contentType\": {\n \"description\": \"Optional MIME type hint for the consumer UI (e.g. \\\"text/plain\\\", \\\"application/x-pem-file\\\").\",\n \"type\": \"string\"\n },\n \"id\": {\n \"description\": \"Opaque maintainer-assigned id for this attachment; used to fetch the blob via a separate mechanism.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"name\": {\n \"description\": \"User-supplied filename (e.g. \\\"recovery-codes.txt\\\").\",\n \"maxLength\": 256,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"sha256\": {\n \"description\": \"Hex-encoded SHA-256 of the encrypted blob bytes (post-encryption). Lets the consumer verify integrity after fetch.\",\n \"pattern\": \"^[0-9a-f]{64}$\",\n \"type\": \"string\"\n },\n \"sizeBytes\": {\n \"description\": \"Size of the encrypted blob in bytes. Maintainers MAY enforce a maximum per attachment and per entry.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"id\",\n \"name\",\n \"sizeBytes\",\n \"sha256\"\n ],\n \"title\": \"AttachmentRef\",\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 \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"The success response to a vault/list request. Carried in a Trust Task document whose type is https://trusttasks.org/spec/vault/list/0.1#response.\",\n \"properties\": {\n \"cursor\": {\n \"description\": \"Opaque continuation token to fetch the next page. Present only when `truncated` is true AND the maintainer supports pagination from this point.\",\n \"type\": \"string\"\n },\n \"entries\": {\n \"description\": \"Matching VaultEntry items in metadata-only view, in maintainer-defined order (RECOMMENDED: most-recently-used first when no other order is implied by the filters). May be empty.\",\n \"items\": {\n \"$ref\": \"#/$defs/VaultEntry\"\n },\n \"type\": \"array\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"redactedFields\": {\n \"description\": \"Names of VaultEntry fields the maintainer redacted from every returned entry (for example, ['lastUsedAt'] when releasing to a consumer whose policy does not permit timing-data exposure).\",\n \"items\": {\n \"type\": \"string\"\n },\n \"type\": \"array\"\n },\n \"truncated\": {\n \"description\": \"true when more matching entries exist beyond `entries`; false when this response is the complete result. Independent of `cursor`: a maintainer MAY truncate without supporting pagination, in which case `truncated` is true and `cursor` is absent.\",\n \"type\": \"boolean\"\n }\n },\n \"required\": [\n \"entries\",\n \"truncated\"\n ],\n \"title\": \"Vault List — response payload\",\n \"type\": \"object\"\n },\n \"SecretKind\": {\n \"description\": \"Discriminator for the secret type stored in the entry. Definitions:\\n- `password` — username + password (+ optional TOTP seed).\\n- `passkey` — WebAuthn discoverable credential (private key + rpId + userHandle).\\n- `oauth-tokens` — OAuth 2.0 refresh + access token bundle for a specific provider.\\n- `did-self-issued` — Self-Issued OpenID Provider v2 (SIOP) credential: the entry points at a DID + signing key already managed by the VTA.\\n- `didcomm-peer` — DIDComm peer identity used to authenticate against a DIDComm-speaking relying party.\\n- `bearer-token` — opaque bearer token carried in a maintainer-named header (covers API tokens, long-lived JWTs, personal-access tokens).\\n- `ssh-key` — SSH private key + comment.\\n- `custom` — arbitrary structured fields; release-time consumer responsible for interpretation.\",\n \"enum\": [\n \"password\",\n \"passkey\",\n \"oauth-tokens\",\n \"did-self-issued\",\n \"didcomm-peer\",\n \"bearer-token\",\n \"ssh-key\",\n \"custom\"\n ],\n \"title\": \"SecretKind\",\n \"type\": \"string\"\n },\n \"SiteTarget\": {\n \"description\": \"A single binding target for a vault entry. Tagged union over the discriminator `kind`. A VaultEntry's `targets` array MAY mix any number of these.\",\n \"oneOf\": [\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"kind\": {\n \"const\": \"web-origin\"\n },\n \"origin\": {\n \"description\": \"Web origin per RFC 6454 (scheme + host + optional port), e.g. \\\"https://github.com\\\". Compared by exact string equality after canonicalisation (lowercase host, default port elided). Consumers wanting subdomain coverage SHOULD add multiple targets, not encode a wildcard.\",\n \"format\": \"uri\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"kind\",\n \"origin\"\n ],\n \"title\": \"WebOrigin\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"did\": {\n \"description\": \"DID identifying the relying party (e.g. did:web:rp.example). The vault maintainer is responsible for any DID resolution required to act on this entry.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"kind\": {\n \"const\": \"did\"\n }\n },\n \"required\": [\n \"kind\",\n \"did\"\n ],\n \"title\": \"Did\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"bundleId\": {\n \"description\": \"iOS bundle identifier in reverse-DNS form (e.g. \\\"com.github.stwalkerster.codehub\\\"). Compared by exact string equality. Matches when an iOS Companion identifies the requesting app via its bundle id (typically via the OS Credential Manager integration).\",\n \"minLength\": 1,\n \"pattern\": \"^[A-Za-z0-9.-]+$\",\n \"type\": \"string\"\n },\n \"kind\": {\n \"const\": \"ios-app\"\n },\n \"teamId\": {\n \"description\": \"Optional Apple Developer Team identifier (10-character alphanumeric). When supplied, the maintainer SHOULD also verify the team id of the requesting app before matching — defense in depth against bundle-id squatting on jailbroken devices.\",\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"kind\",\n \"bundleId\"\n ],\n \"title\": \"IosApp\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"kind\": {\n \"const\": \"android-app\"\n },\n \"packageName\": {\n \"description\": \"Android package name in reverse-DNS form (e.g. \\\"com.github.android\\\").\",\n \"minLength\": 1,\n \"pattern\": \"^[A-Za-z][A-Za-z0-9_]*(\\\\.[A-Za-z][A-Za-z0-9_]*)+$\",\n \"type\": \"string\"\n },\n \"sha256CertFingerprints\": {\n \"description\": \"SHA-256 fingerprints of the app's signing certificates, in colon-separated hex (the format `apksigner` and the Play Console emit). At least one fingerprint MUST be present. The maintainer matches when ANY of the provided fingerprints matches the requesting app's signature — this supports apps signed by multiple keys (e.g. during certificate rotation via Play App Signing).\",\n \"items\": {\n \"pattern\": \"^[0-9A-F]{2}(:[0-9A-F]{2}){31}$\",\n \"type\": \"string\"\n },\n \"minItems\": 1,\n \"type\": \"array\",\n \"uniqueItems\": true\n }\n },\n \"required\": [\n \"kind\",\n \"packageName\",\n \"sha256CertFingerprints\"\n ],\n \"title\": \"AndroidApp\",\n \"type\": \"object\"\n }\n ],\n \"title\": \"SiteTarget\"\n },\n \"VaultEntry\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"attachments\": {\n \"description\": \"References to encrypted blobs associated with the entry (recovery codes, PEM files, screenshots of authenticator setup). The blobs themselves are fetched via a separate mechanism the maintainer documents; metadata view exposes only the descriptor.\",\n \"items\": {\n \"$ref\": \"#/$defs/AttachmentRef\"\n },\n \"type\": \"array\"\n },\n \"breachedAt\": {\n \"description\": \"Set by the maintainer (via HIBP integration or equivalent) when the password material associated with this entry is known to appear in a public breach. Consumers SHOULD surface this prominently. Cleared when the user rotates the password and the new password is not in any known breach.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"contextId\": {\n \"description\": \"Identifier of the trust context (persona) the entry belongs to. Opaque string interpreted by the vault maintainer; corresponds to a single ContextRecord on the VTA side.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"createdAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"createdBy\": {\n \"description\": \"VID of the consumer that originally created the entry.\",\n \"type\": \"string\"\n },\n \"customFieldNames\": {\n \"description\": \"Names of additional fields the user has attached (e.g. [\\\"security-question-1\\\", \\\"account-number\\\"]). The VALUES live in the secret payload and are only delivered by vault/release/0.1. Exposing names in metadata lets the consumer render the right form layout before requesting release.\",\n \"items\": {\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"expiresAt\": {\n \"description\": \"Optional time after which the credential is no longer expected to be valid (e.g. an OAuth refresh token's known expiry, a time-limited API token, an enterprise password rotation policy). Maintainers MAY surface this in the consumer UI as a warning.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1. Reverse-DNS-namespaced; consumers MUST ignore unrecognized namespaces.\"\n },\n \"favicon\": {\n \"description\": \"Optional URI of an icon to display in the consumer UI. Maintainers MAY fetch and cache; consumers SHOULD treat as untrusted content and fetch via a sandboxed pipeline.\",\n \"format\": \"uri\",\n \"type\": \"string\"\n },\n \"id\": {\n \"description\": \"Opaque vault-maintainer-assigned identifier for the entry. ULID/UUID/base32 are common; the wire spec only requires non-empty string equality.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"label\": {\n \"description\": \"Human-readable display name (e.g. \\\"Work GitHub\\\", \\\"Personal bank — checking\\\"). Maintainers MAY enforce a maximum length; the wire spec does not.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"lastUsedAt\": {\n \"description\": \"Most recent time the entry was used (either released or proxy-login performed). Maintainers MAY return this with reduced precision (e.g. hour-floored) when releasing to a less-trusted consumer.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"notes\": {\n \"description\": \"Non-sensitive notes the user attached to the entry. Visible in metadata view (suitable for support contact, account number, expiry policy memos). SENSITIVE notes belong in the secret payload as a `secureNotes` field — those are only released by vault/release/0.1.\",\n \"maxLength\": 4096,\n \"type\": \"string\"\n },\n \"passwordChangedAt\": {\n \"description\": \"Set whenever the password component of the secret payload is rotated. Maintainers MUST update this on every secret-material change for entries of kind `password` (or any kind that carries a password component). Used by consumers to surface rotation-overdue warnings.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"principalDid\": {\n \"description\": \"Optional cached DID the entry will act AS for DID-shaped flows — mirrors the `did` field of the entry's secret payload when `secretKind` carries one (`did-self-issued`, `didcomm-peer`). Absent for kinds that have no DID concept (`password`, `passkey`, `oauth-tokens`, `bearer-token`, `ssh-key`, `custom`). MAINTAINER-DERIVED, NOT CONSUMER-SUPPLIED: the maintainer MUST recompute this from the canonical secret at every upsert / secret rotation; a producer-supplied value on `vault/upsert/0.1` MUST be ignored (no error, but no honour). Read-only on the wire, present in metadata views so consumers can drive RP-side flows (e.g. fetch `/auth/challenge` keyed on the principal DID before requesting a proxy-login) without releasing the secret.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"secretKind\": {\n \"$ref\": \"#/$defs/SecretKind\",\n \"description\": \"Discriminator for the kind of secret this entry holds. The secret material itself is NEVER returned in metadata views; the kind is exposed so consumers can render an appropriate UI affordance and so policy decisions can route by kind.\"\n },\n \"selectors\": {\n \"description\": \"Opaque maintainer-defined selector strings fed to the policy engine when this entry is requested (e.g. \\\"recent_uv_required\\\", \\\"network_class=corp\\\", \\\"step_up_push\\\"). Consumers MUST treat selectors as opaque; they exist for policy authoring on the maintainer side.\",\n \"items\": {\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"tags\": {\n \"description\": \"User-defined tags for organisation and filtering (e.g. [\\\"family\\\", \\\"finance\\\"]). Maintainers MAY enforce a maximum count; the wire spec does not.\",\n \"items\": {\n \"maxLength\": 64,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"targets\": {\n \"description\": \"One or more binding targets — web origins, mobile app identifiers, and/or DIDs — that this credential applies to. A request from any matching target uses this entry. A typical entry for a service that exists as both a website and mobile apps will list a web origin, an iOS bundle id, and an Android package id; passkeys for that service typically list only the origin (because iOS Associated Domains and Android Asset Links bind apps to the domain at the OS level).\",\n \"items\": {\n \"$ref\": \"#/$defs/SiteTarget\"\n },\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"updatedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"updatedBy\": {\n \"description\": \"VID of the consumer that last modified the entry.\",\n \"type\": \"string\"\n },\n \"version\": {\n \"description\": \"Monotonic version counter incremented on every mutation. Used by consumers for optimistic-concurrency checks on vault/upsert and as the seq baseline for vault/sync.\",\n \"minimum\": 0,\n \"type\": \"integer\"\n }\n },\n \"required\": [\n \"id\",\n \"contextId\",\n \"targets\",\n \"label\",\n \"secretKind\",\n \"createdAt\",\n \"updatedAt\",\n \"version\"\n ],\n \"title\": \"VaultEntry\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/vault/list/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"Query a vault maintainer for the metadata view of stored entries. All filters are AND-combined. Pagination is opaque-cursor based; the maintainer chooses page size within the caller's bound and its own ceiling. Secret material is NEVER returned by this task — even when the requesting consumer has VaultRead capability for the entry, the secret is only released via vault/release/0.1.\",\n \"properties\": {\n \"breached\": {\n \"description\": \"Optional filter — when true, only entries with `breachedAt` set are returned; when false, only entries with `breachedAt` absent are returned.\",\n \"type\": \"boolean\"\n },\n \"contextId\": {\n \"description\": \"Optional filter — only entries belonging to this trust context (persona) are returned. Omit to query across all contexts the requesting consumer has VaultRead on.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"cursor\": {\n \"description\": \"Opaque continuation token returned by the maintainer in a previous response. Consumers MUST treat the cursor as opaque and re-send it verbatim.\",\n \"type\": \"string\"\n },\n \"expiresBefore\": {\n \"description\": \"Optional filter — only entries whose `expiresAt` is less than this timestamp are returned. Useful for \\\"what's about to expire\\\" panels.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"neverUsed\": {\n \"description\": \"Optional filter — when true, only entries with no `lastUsedAt` are returned. Mutually exclusive with `usedSince`; maintainers MUST reject documents that supply both.\",\n \"type\": \"boolean\"\n },\n \"pageSize\": {\n \"description\": \"Maximum number of entries to return. Maintainer-defined default and ceiling; the maintainer MAY return fewer.\",\n \"maximum\": 1000,\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"secretKind\": {\n \"$ref\": \"#/$defs/SecretKind\",\n \"description\": \"Optional filter — only entries of this secret kind are returned.\"\n },\n \"tag\": {\n \"description\": \"Optional filter — only entries whose `tags` array contains this tag.\",\n \"maxLength\": 64,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"targetAndroidPackage\": {\n \"description\": \"Optional filter — only entries with at least one `targets[]` entry of kind `android-app` whose `packageName` exactly equals this value. The typical Android Companion lookup.\",\n \"minLength\": 1,\n \"pattern\": \"^[A-Za-z][A-Za-z0-9_]*(\\\\.[A-Za-z][A-Za-z0-9_]*)+$\",\n \"type\": \"string\"\n },\n \"targetDid\": {\n \"description\": \"Optional filter — only entries with at least one `targets[]` entry of kind `did` exactly equal to this value.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"targetIosBundleId\": {\n \"description\": \"Optional filter — only entries with at least one `targets[]` entry of kind `ios-app` whose `bundleId` exactly equals this value. The typical iOS Companion lookup.\",\n \"minLength\": 1,\n \"pattern\": \"^[A-Za-z0-9.-]+$\",\n \"type\": \"string\"\n },\n \"targetOriginPrefix\": {\n \"description\": \"Optional filter — only entries with at least one `targets[]` entry of kind `web-origin` whose origin starts with this prefix. Useful for \\\"all my github.* logins\\\". Maintainers MUST canonicalise (lowercase host) before comparison.\",\n \"format\": \"uri\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"usedSince\": {\n \"description\": \"Optional filter — only entries whose `lastUsedAt` is greater than or equal to this timestamp are returned. Entries that have never been used are excluded when this filter is present.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n }\n },\n \"title\": \"Vault List — 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).
#[test]
fn response_example_1() {
const JSON: &str = "{\n \"id\": \"vlist-resp-5678-9012-34ef-56789012345\",\n \"type\": \"https://trusttasks.org/spec/vault/list/0.1#response\",\n \"threadId\": \"vlist-1234-5678-90ab-cdef12345678\",\n \"issuer\": \"did:web:vta.example\",\n \"recipient\": \"did:peer:2.Ez6LSc…\",\n \"issuedAt\": \"2026-05-26T10:00:00Z\",\n \"payload\": {\n \"entries\": [\n {\n \"id\": \"vault_01HZX2QY8E0F4Q3V5W7B9N2K6S\",\n \"contextId\": \"ctx_work\",\n \"targets\": [\n { \"kind\": \"web-origin\", \"origin\": \"https://github.com\" },\n { \"kind\": \"ios-app\", \"bundleId\": \"com.github.stwalkerster.codehub\", \"teamId\": \"VEKTX9H2N7\" },\n { \"kind\": \"android-app\", \"packageName\": \"com.github.android\",\n \"sha256CertFingerprints\": [\"12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0:12:34:56:78:9A:BC:DE:F0\"] }\n ],\n \"label\": \"Work GitHub\",\n \"secretKind\": \"passkey\",\n \"tags\": [\"work\", \"engineering\"],\n \"selectors\": [\"recent_uv_required\"],\n \"createdAt\": \"2026-02-14T09:30:00Z\",\n \"createdBy\": \"did:peer:2.Ez6LSc…\",\n \"updatedAt\": \"2026-04-02T14:15:00Z\",\n \"lastUsedAt\": \"2026-05-25T22:11:00Z\",\n \"passwordChangedAt\": \"2026-04-02T14:15:00Z\",\n \"version\": 7\n },\n {\n \"id\": \"vault_01HZX2R0F1G5R4W6X8C0P3L7T\",\n \"contextId\": \"ctx_work\",\n \"targets\": [\n { \"kind\": \"web-origin\", \"origin\": \"https://aws.amazon.com\" }\n ],\n \"label\": \"Work AWS — root\",\n \"secretKind\": \"password\",\n \"tags\": [\"work\", \"high-value\"],\n \"notes\": \"Recovery email: ops@example.com\",\n \"selectors\": [\"step_up_push\", \"high_value\"],\n \"createdAt\": \"2026-01-08T11:00:00Z\",\n \"updatedAt\": \"2026-05-01T08:00:00Z\",\n \"lastUsedAt\": \"2026-05-26T09:30:00Z\",\n \"passwordChangedAt\": \"2026-05-01T08:00:00Z\",\n \"breachedAt\": \"2026-04-22T00:00:00Z\",\n \"version\": 3\n }\n ],\n \"truncated\": true,\n \"cursor\": \"opaque-maintainer-cursor-string\"\n }\n}\n";
let doc: crate::TrustTask<super::Response> =
serde_json::from_str(JSON).expect("deserialize response example");
let rendered = serde_json::to_value(&doc).expect("re-serialize");
let expected: serde_json::Value = serde_json::from_str(JSON).expect("re-parse expected");
assert_eq!(rendered, expected, "response example failed round-trip");
}
#[test]
fn response_example_2() {
const JSON: &str = "{\n \"id\": \"vlist-resp-6789-0123-45f0-678901234567\",\n \"type\": \"https://trusttasks.org/spec/vault/list/0.1#response\",\n \"threadId\": \"vlist-4567-8901-23de-f4567890123\",\n \"issuer\": \"did:web:vta.example\",\n \"recipient\": \"did:peer:2.Ez6LSc…\",\n \"issuedAt\": \"2026-05-26T10:03:01Z\",\n \"payload\": {\n \"entries\": [],\n \"truncated\": false\n }\n}\n";
let doc: crate::TrustTask<super::Response> =
serde_json::from_str(JSON).expect("deserialize response example");
let rendered = serde_json::to_value(&doc).expect("re-serialize");
let expected: serde_json::Value = serde_json::from_str(JSON).expect("re-parse expected");
assert_eq!(rendered, expected, "response example failed round-trip");
}
}