//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `device/register`. 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())
}
}
}
///Fine-grained capability flag scoped to the device's allowed contexts. See SPEC.md for the full semantics of each.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Capability",
/// "description": "Fine-grained capability flag scoped to the device's allowed contexts. See SPEC.md for the full semantics of each.",
/// "type": "string",
/// "enum": [
/// "vault-read",
/// "vault-write",
/// "proxy-login",
/// "fill-release",
/// "policy-admin",
/// "device-admin",
/// "sign",
/// "key-mint"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum Capability {
#[serde(rename = "vault-read")]
VaultRead,
#[serde(rename = "vault-write")]
VaultWrite,
#[serde(rename = "proxy-login")]
ProxyLogin,
#[serde(rename = "fill-release")]
FillRelease,
#[serde(rename = "policy-admin")]
PolicyAdmin,
#[serde(rename = "device-admin")]
DeviceAdmin,
#[serde(rename = "sign")]
Sign,
#[serde(rename = "key-mint")]
KeyMint,
}
impl ::std::convert::From<&Self> for Capability {
fn from(value: &Capability) -> Self {
value.clone()
}
}
impl ::std::fmt::Display for Capability {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::VaultRead => f.write_str("vault-read"),
Self::VaultWrite => f.write_str("vault-write"),
Self::ProxyLogin => f.write_str("proxy-login"),
Self::FillRelease => f.write_str("fill-release"),
Self::PolicyAdmin => f.write_str("policy-admin"),
Self::DeviceAdmin => f.write_str("device-admin"),
Self::Sign => f.write_str("sign"),
Self::KeyMint => f.write_str("key-mint"),
}
}
}
impl ::std::str::FromStr for Capability {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"vault-read" => Ok(Self::VaultRead),
"vault-write" => Ok(Self::VaultWrite),
"proxy-login" => Ok(Self::ProxyLogin),
"fill-release" => Ok(Self::FillRelease),
"policy-admin" => Ok(Self::PolicyAdmin),
"device-admin" => Ok(Self::DeviceAdmin),
"sign" => Ok(Self::Sign),
"key-mint" => Ok(Self::KeyMint),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Capability {
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 Capability {
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 Capability {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Discriminator: is this consumer a user-driven Companion or a headless Service?
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ConsumerKind",
/// "description": "Discriminator: is this consumer a user-driven Companion or a headless Service?",
/// "oneOf": [
/// {
/// "title": "Companion",
/// "type": "object",
/// "required": [
/// "formFactor",
/// "kind"
/// ],
/// "properties": {
/// "formFactor": {
/// "type": "string",
/// "enum": [
/// "browser",
/// "mobile",
/// "desktop"
/// ]
/// },
/// "kind": {
/// "const": "companion"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "Service",
/// "type": "object",
/// "required": [
/// "kind",
/// "serviceKind"
/// ],
/// "properties": {
/// "kind": {
/// "const": "service"
/// },
/// "serviceKind": {
/// "type": "string",
/// "enum": [
/// "mediator",
/// "ai-agent",
/// "daemon"
/// ]
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(tag = "kind", deny_unknown_fields)]
pub enum ConsumerKind {
///Companion
#[serde(rename = "companion")]
Companion {
#[serde(rename = "formFactor")]
form_factor: ConsumerKindFormFactor,
},
///Service
#[serde(rename = "service")]
Service {
#[serde(rename = "serviceKind")]
service_kind: ConsumerKindServiceKind,
},
}
impl ::std::convert::From<&Self> for ConsumerKind {
fn from(value: &ConsumerKind) -> Self {
value.clone()
}
}
///`ConsumerKindFormFactor`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "browser",
/// "mobile",
/// "desktop"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum ConsumerKindFormFactor {
#[serde(rename = "browser")]
Browser,
#[serde(rename = "mobile")]
Mobile,
#[serde(rename = "desktop")]
Desktop,
}
impl ::std::convert::From<&Self> for ConsumerKindFormFactor {
fn from(value: &ConsumerKindFormFactor) -> Self {
value.clone()
}
}
impl ::std::fmt::Display for ConsumerKindFormFactor {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Browser => f.write_str("browser"),
Self::Mobile => f.write_str("mobile"),
Self::Desktop => f.write_str("desktop"),
}
}
}
impl ::std::str::FromStr for ConsumerKindFormFactor {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"browser" => Ok(Self::Browser),
"mobile" => Ok(Self::Mobile),
"desktop" => Ok(Self::Desktop),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ConsumerKindFormFactor {
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 ConsumerKindFormFactor {
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 ConsumerKindFormFactor {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`ConsumerKindServiceKind`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "mediator",
/// "ai-agent",
/// "daemon"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum ConsumerKindServiceKind {
#[serde(rename = "mediator")]
Mediator,
#[serde(rename = "ai-agent")]
AiAgent,
#[serde(rename = "daemon")]
Daemon,
}
impl ::std::convert::From<&Self> for ConsumerKindServiceKind {
fn from(value: &ConsumerKindServiceKind) -> Self {
value.clone()
}
}
impl ::std::fmt::Display for ConsumerKindServiceKind {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Mediator => f.write_str("mediator"),
Self::AiAgent => f.write_str("ai-agent"),
Self::Daemon => f.write_str("daemon"),
}
}
}
impl ::std::str::FromStr for ConsumerKindServiceKind {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"mediator" => Ok(Self::Mediator),
"ai-agent" => Ok(Self::AiAgent),
"daemon" => Ok(Self::Daemon),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ConsumerKindServiceKind {
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 ConsumerKindServiceKind {
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 ConsumerKindServiceKind {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Producer-supplied attestation at registration time, verifiable by the maintainer against the platform's attestation infrastructure. Tagged union over the discriminator `kind`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DeviceAttestation",
/// "description": "Producer-supplied attestation at registration time, verifiable by the maintainer against the platform's attestation infrastructure. Tagged union over the discriminator `kind`.",
/// "oneOf": [
/// {
/// "title": "WebAuthnAttestation",
/// "type": "object",
/// "required": [
/// "aaguid",
/// "kind"
/// ],
/// "properties": {
/// "aaguid": {
/// "description": "WebAuthn Authenticator AAGUID (UUID).",
/// "type": "string",
/// "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
/// },
/// "attestationStatement": {
/// "description": "Base64url-encoded WebAuthn attestation statement, when supplied by the platform.",
/// "type": "string"
/// },
/// "kind": {
/// "const": "webauthn"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "AppleAppAttest",
/// "type": "object",
/// "required": [
/// "attestation",
/// "keyId",
/// "kind"
/// ],
/// "properties": {
/// "attestation": {
/// "type": "string"
/// },
/// "keyId": {
/// "type": "string",
/// "minLength": 1
/// },
/// "kind": {
/// "const": "apple-app-attest"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "PlayIntegrity",
/// "type": "object",
/// "required": [
/// "kind",
/// "token"
/// ],
/// "properties": {
/// "kind": {
/// "const": "play-integrity"
/// },
/// "token": {
/// "type": "string",
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "Tpm",
/// "type": "object",
/// "required": [
/// "kind",
/// "quote"
/// ],
/// "properties": {
/// "kind": {
/// "const": "tpm"
/// },
/// "quote": {
/// "type": "string"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "NitroEnclave",
/// "type": "object",
/// "required": [
/// "kind",
/// "quote"
/// ],
/// "properties": {
/// "kind": {
/// "const": "nitro-enclave"
/// },
/// "quote": {
/// "type": "string"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "title": "NoAttestation",
/// "description": "No device-level attestation is available. Maintainers MAY still register the device but SHOULD apply stricter policy (shorter session TTL, more frequent step-up).",
/// "type": "object",
/// "required": [
/// "kind"
/// ],
/// "properties": {
/// "kind": {
/// "const": "none"
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(tag = "kind", deny_unknown_fields)]
pub enum DeviceAttestation {
///WebAuthnAttestation
#[serde(rename = "webauthn")]
Webauthn {
///WebAuthn Authenticator AAGUID (UUID).
aaguid: DeviceAttestationAaguid,
///Base64url-encoded WebAuthn attestation statement, when supplied by the platform.
#[serde(
rename = "attestationStatement",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
attestation_statement: ::std::option::Option<::std::string::String>,
},
///AppleAppAttest
#[serde(rename = "apple-app-attest")]
AppleAppAttest {
attestation: ::std::string::String,
#[serde(rename = "keyId")]
key_id: DeviceAttestationKeyId,
},
///PlayIntegrity
#[serde(rename = "play-integrity")]
PlayIntegrity { token: DeviceAttestationToken },
///Tpm
#[serde(rename = "tpm")]
Tpm { quote: ::std::string::String },
///NitroEnclave
#[serde(rename = "nitro-enclave")]
NitroEnclave { quote: ::std::string::String },
#[serde(rename = "none")]
None,
}
impl ::std::convert::From<&Self> for DeviceAttestation {
fn from(value: &DeviceAttestation) -> Self {
value.clone()
}
}
///WebAuthn Authenticator AAGUID (UUID).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "WebAuthn Authenticator AAGUID (UUID).",
/// "type": "string",
/// "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DeviceAttestationAaguid(::std::string::String);
impl ::std::ops::Deref for DeviceAttestationAaguid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeviceAttestationAaguid> for ::std::string::String {
fn from(value: DeviceAttestationAaguid) -> Self {
value.0
}
}
impl ::std::convert::From<&DeviceAttestationAaguid> for DeviceAttestationAaguid {
fn from(value: &DeviceAttestationAaguid) -> Self {
value.clone()
}
}
impl ::std::str::FromStr for DeviceAttestationAaguid {
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]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
)
.unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DeviceAttestationAaguid {
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 DeviceAttestationAaguid {
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 DeviceAttestationAaguid {
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 DeviceAttestationAaguid {
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())
})
}
}
///`DeviceAttestationKeyId`
///
/// <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 DeviceAttestationKeyId(::std::string::String);
impl ::std::ops::Deref for DeviceAttestationKeyId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeviceAttestationKeyId> for ::std::string::String {
fn from(value: DeviceAttestationKeyId) -> Self {
value.0
}
}
impl ::std::convert::From<&DeviceAttestationKeyId> for DeviceAttestationKeyId {
fn from(value: &DeviceAttestationKeyId) -> Self {
value.clone()
}
}
impl ::std::str::FromStr for DeviceAttestationKeyId {
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 DeviceAttestationKeyId {
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 DeviceAttestationKeyId {
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 DeviceAttestationKeyId {
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 DeviceAttestationKeyId {
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())
})
}
}
///`DeviceAttestationToken`
///
/// <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 DeviceAttestationToken(::std::string::String);
impl ::std::ops::Deref for DeviceAttestationToken {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeviceAttestationToken> for ::std::string::String {
fn from(value: DeviceAttestationToken) -> Self {
value.0
}
}
impl ::std::convert::From<&DeviceAttestationToken> for DeviceAttestationToken {
fn from(value: &DeviceAttestationToken) -> Self {
value.clone()
}
}
impl ::std::str::FromStr for DeviceAttestationToken {
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 DeviceAttestationToken {
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 DeviceAttestationToken {
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 DeviceAttestationToken {
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 DeviceAttestationToken {
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())
})
}
}
///`DeviceBinding`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "DeviceBinding",
/// "type": "object",
/// "required": [
/// "consumerDid",
/// "consumerKind",
/// "deviceId",
/// "displayName",
/// "registeredAt"
/// ],
/// "properties": {
/// "attestation": {
/// "$ref": "#/definitions/DeviceAttestation"
/// },
/// "capabilities": {
/// "description": "Capability bitset granted to this device (mirrors the ACL-side scope). Returned for inspection; mutated only via acl/change-role or device/disable.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/Capability"
/// },
/// "uniqueItems": true
/// },
/// "consumerDid": {
/// "description": "The long-term VTA-derived key (DID) the device authenticates with. Established via the ACL-swap pattern at registration.",
/// "type": "string",
/// "minLength": 1
/// },
/// "consumerKind": {
/// "$ref": "#/definitions/ConsumerKind"
/// },
/// "deviceId": {
/// "description": "Maintainer-assigned opaque id for this device. Stable across the device's lifetime — never re-used after disable or wipe.",
/// "type": "string",
/// "minLength": 1
/// },
/// "disabledAt": {
/// "description": "Present when the device has been disabled (device/disable). Disabled devices cannot authenticate but their record is retained for audit.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "displayName": {
/// "description": "Human-readable name (e.g. \"Glenn's MacBook — Chrome\", \"iPhone 17\").",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "lastSeenAt": {
/// "description": "Updated on every device/heartbeat and on any successful auth.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "platform": {
/// "description": "Free-form platform descriptor (e.g. \"macOS 16 / Chrome 142\", \"iOS 19.1\", \"Android 16\", \"Linux/x86_64\"). Producer-supplied at registration; consumer-supplied updates are accepted on heartbeat.",
/// "type": "string"
/// },
/// "pushCapable": {
/// "description": "Whether this device has registered a push channel with its mediator via the push binding's `set-device-info` (https://trusttasks.org/binding/push/0.1). Informational visibility for device/list only — the actual push token is held by the mediator, never by the maintainer/VTA. The maintainer sets this from a signal supplied by its mediator, not from device-supplied registration data.",
/// "type": "boolean"
/// },
/// "registeredAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "wipedAt": {
/// "description": "Present when a wipe has been issued (device/wipe). Distinct from disabledAt — a wiped device is also disabled, but wipe additionally communicates a wipe-cache instruction that the device may or may not have executed (see device/wipe).",
/// "type": "string",
/// "format": "date-time"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct DeviceBinding {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub attestation: ::std::option::Option<DeviceAttestation>,
///Capability bitset granted to this device (mirrors the ACL-side scope). Returned for inspection; mutated only via acl/change-role or device/disable.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub capabilities: ::std::option::Option<Vec<Capability>>,
///The long-term VTA-derived key (DID) the device authenticates with. Established via the ACL-swap pattern at registration.
#[serde(rename = "consumerDid")]
pub consumer_did: DeviceBindingConsumerDid,
#[serde(rename = "consumerKind")]
pub consumer_kind: ConsumerKind,
///Maintainer-assigned opaque id for this device. Stable across the device's lifetime — never re-used after disable or wipe.
#[serde(rename = "deviceId")]
pub device_id: DeviceBindingDeviceId,
///Present when the device has been disabled (device/disable). Disabled devices cannot authenticate but their record is retained for audit.
#[serde(
rename = "disabledAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub disabled_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Human-readable name (e.g. "Glenn's MacBook — Chrome", "iPhone 17").
#[serde(rename = "displayName")]
pub display_name: DeviceBindingDisplayName,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Updated on every device/heartbeat and on any successful auth.
#[serde(
rename = "lastSeenAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub last_seen_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///Free-form platform descriptor (e.g. "macOS 16 / Chrome 142", "iOS 19.1", "Android 16", "Linux/x86_64"). Producer-supplied at registration; consumer-supplied updates are accepted on heartbeat.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub platform: ::std::option::Option<::std::string::String>,
///Whether this device has registered a push channel with its mediator via the push binding's `set-device-info` (https://trusttasks.org/binding/push/0.1). Informational visibility for device/list only — the actual push token is held by the mediator, never by the maintainer/VTA. The maintainer sets this from a signal supplied by its mediator, not from device-supplied registration data.
#[serde(
rename = "pushCapable",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub push_capable: ::std::option::Option<bool>,
#[serde(rename = "registeredAt")]
pub registered_at: ::chrono::DateTime<::chrono::offset::Utc>,
///Present when a wipe has been issued (device/wipe). Distinct from disabledAt — a wiped device is also disabled, but wipe additionally communicates a wipe-cache instruction that the device may or may not have executed (see device/wipe).
#[serde(
rename = "wipedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub wiped_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
}
impl ::std::convert::From<&DeviceBinding> for DeviceBinding {
fn from(value: &DeviceBinding) -> Self {
value.clone()
}
}
///The long-term VTA-derived key (DID) the device authenticates with. Established via the ACL-swap pattern at registration.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The long-term VTA-derived key (DID) the device authenticates with. Established via the ACL-swap pattern at registration.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DeviceBindingConsumerDid(::std::string::String);
impl ::std::ops::Deref for DeviceBindingConsumerDid {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeviceBindingConsumerDid> for ::std::string::String {
fn from(value: DeviceBindingConsumerDid) -> Self {
value.0
}
}
impl ::std::convert::From<&DeviceBindingConsumerDid> for DeviceBindingConsumerDid {
fn from(value: &DeviceBindingConsumerDid) -> Self {
value.clone()
}
}
impl ::std::str::FromStr for DeviceBindingConsumerDid {
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 DeviceBindingConsumerDid {
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 DeviceBindingConsumerDid {
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 DeviceBindingConsumerDid {
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 DeviceBindingConsumerDid {
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())
})
}
}
///Maintainer-assigned opaque id for this device. Stable across the device's lifetime — never re-used after disable or wipe.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Maintainer-assigned opaque id for this device. Stable across the device's lifetime — never re-used after disable or wipe.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DeviceBindingDeviceId(::std::string::String);
impl ::std::ops::Deref for DeviceBindingDeviceId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeviceBindingDeviceId> for ::std::string::String {
fn from(value: DeviceBindingDeviceId) -> Self {
value.0
}
}
impl ::std::convert::From<&DeviceBindingDeviceId> for DeviceBindingDeviceId {
fn from(value: &DeviceBindingDeviceId) -> Self {
value.clone()
}
}
impl ::std::str::FromStr for DeviceBindingDeviceId {
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 DeviceBindingDeviceId {
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 DeviceBindingDeviceId {
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 DeviceBindingDeviceId {
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 DeviceBindingDeviceId {
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 name (e.g. "Glenn's MacBook — Chrome", "iPhone 17").
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Human-readable name (e.g. \"Glenn's MacBook — Chrome\", \"iPhone 17\").",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DeviceBindingDisplayName(::std::string::String);
impl ::std::ops::Deref for DeviceBindingDisplayName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeviceBindingDisplayName> for ::std::string::String {
fn from(value: DeviceBindingDisplayName) -> Self {
value.0
}
}
impl ::std::convert::From<&DeviceBindingDisplayName> for DeviceBindingDisplayName {
fn from(value: &DeviceBindingDisplayName) -> Self {
value.clone()
}
}
impl ::std::str::FromStr for DeviceBindingDisplayName {
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 DeviceBindingDisplayName {
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 DeviceBindingDisplayName {
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 DeviceBindingDisplayName {
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 DeviceBindingDisplayName {
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<&Ext> for Ext {
fn from(value: &Ext) -> Self {
value.clone()
}
}
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::convert::From<&ExtKey> for ExtKey {
fn from(value: &ExtKey) -> Self {
value.clone()
}
}
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())
})
}
}
///Public discovery surface that wraps the maintainer's existing two-phase enrolment (provision-integration → acl/swap-key). A new Companion or Service hands the maintainer its long-term VTA-derived key, its consumer kind, display name, and an optional device attestation; the maintainer creates the DeviceBinding and returns it. Phase 1 (provision-integration) is assumed to have already happened — this task is the post-bootstrap claim step.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/device/register/0.1",
/// "title": "Payload",
/// "description": "Public discovery surface that wraps the maintainer's existing two-phase enrolment (provision-integration → acl/swap-key). A new Companion or Service hands the maintainer its long-term VTA-derived key, its consumer kind, display name, and an optional device attestation; the maintainer creates the DeviceBinding and returns it. Phase 1 (provision-integration) is assumed to have already happened — this task is the post-bootstrap claim step.",
/// "type": "object",
/// "required": [
/// "consumerKind",
/// "displayName"
/// ],
/// "properties": {
/// "attestation": {
/// "$ref": "#/definitions/DeviceAttestation"
/// },
/// "consumerKind": {
/// "$ref": "#/definitions/ConsumerKind"
/// },
/// "displayName": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "hpkePublicKey": {
/// "description": "X25519 public key (did:key form) the maintainer will use to HPKE-seal sensitive payloads to this device (sealed secrets, session blobs, sync events). REQUIRED — every Companion/Service needs a recipient key.",
/// "type": "string",
/// "minLength": 1
/// },
/// "platform": {
/// "type": "string"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Payload {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub attestation: ::std::option::Option<DeviceAttestation>,
#[serde(rename = "consumerKind")]
pub consumer_kind: ConsumerKind,
#[serde(rename = "displayName")]
pub display_name: PayloadDisplayName,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///X25519 public key (did:key form) the maintainer will use to HPKE-seal sensitive payloads to this device (sealed secrets, session blobs, sync events). REQUIRED — every Companion/Service needs a recipient key.
#[serde(
rename = "hpkePublicKey",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub hpke_public_key: ::std::option::Option<PayloadHpkePublicKey>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub platform: ::std::option::Option<::std::string::String>,
}
impl ::std::convert::From<&Payload> for Payload {
fn from(value: &Payload) -> Self {
value.clone()
}
}
///`PayloadDisplayName`
///
/// <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 PayloadDisplayName(::std::string::String);
impl ::std::ops::Deref for PayloadDisplayName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadDisplayName> for ::std::string::String {
fn from(value: PayloadDisplayName) -> Self {
value.0
}
}
impl ::std::convert::From<&PayloadDisplayName> for PayloadDisplayName {
fn from(value: &PayloadDisplayName) -> Self {
value.clone()
}
}
impl ::std::str::FromStr for PayloadDisplayName {
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 PayloadDisplayName {
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 PayloadDisplayName {
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 PayloadDisplayName {
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 PayloadDisplayName {
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())
})
}
}
///X25519 public key (did:key form) the maintainer will use to HPKE-seal sensitive payloads to this device (sealed secrets, session blobs, sync events). REQUIRED — every Companion/Service needs a recipient key.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "X25519 public key (did:key form) the maintainer will use to HPKE-seal sensitive payloads to this device (sealed secrets, session blobs, sync events). REQUIRED — every Companion/Service needs a recipient key.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadHpkePublicKey(::std::string::String);
impl ::std::ops::Deref for PayloadHpkePublicKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadHpkePublicKey> for ::std::string::String {
fn from(value: PayloadHpkePublicKey) -> Self {
value.0
}
}
impl ::std::convert::From<&PayloadHpkePublicKey> for PayloadHpkePublicKey {
fn from(value: &PayloadHpkePublicKey) -> Self {
value.clone()
}
}
impl ::std::str::FromStr for PayloadHpkePublicKey {
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 PayloadHpkePublicKey {
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 PayloadHpkePublicKey {
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 PayloadHpkePublicKey {
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 PayloadHpkePublicKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`Response`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "type": "object",
/// "required": [
/// "binding"
/// ],
/// "properties": {
/// "binding": {
/// "$ref": "#/definitions/DeviceBinding"
/// },
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Response {
pub binding: DeviceBinding,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
}
impl ::std::convert::From<&Response> for Response {
fn from(value: &Response) -> Self {
value.clone()
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/device/register/0.1";
const IS_PROOF_REQUIRED: bool = true;
}
impl crate::Payload for Response {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/device/register/0.1#response";
const IS_PROOF_REQUIRED: bool = true;
}
#[cfg(feature = "validate")]
impl crate::validate::ValidatedPayload for Payload {
const SCHEMA_JSON: &'static str = "{\n \"$defs\": {\n \"Capability\": {\n \"description\": \"Fine-grained capability flag scoped to the device's allowed contexts. See SPEC.md for the full semantics of each.\",\n \"enum\": [\n \"vault-read\",\n \"vault-write\",\n \"proxy-login\",\n \"fill-release\",\n \"policy-admin\",\n \"device-admin\",\n \"sign\",\n \"key-mint\"\n ],\n \"title\": \"Capability\",\n \"type\": \"string\"\n },\n \"ConsumerKind\": {\n \"description\": \"Discriminator: is this consumer a user-driven Companion or a headless Service?\",\n \"oneOf\": [\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"formFactor\": {\n \"enum\": [\n \"browser\",\n \"mobile\",\n \"desktop\"\n ],\n \"type\": \"string\"\n },\n \"kind\": {\n \"const\": \"companion\"\n }\n },\n \"required\": [\n \"kind\",\n \"formFactor\"\n ],\n \"title\": \"Companion\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"kind\": {\n \"const\": \"service\"\n },\n \"serviceKind\": {\n \"enum\": [\n \"mediator\",\n \"ai-agent\",\n \"daemon\"\n ],\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"kind\",\n \"serviceKind\"\n ],\n \"title\": \"Service\",\n \"type\": \"object\"\n }\n ],\n \"title\": \"ConsumerKind\"\n },\n \"DeviceAttestation\": {\n \"description\": \"Producer-supplied attestation at registration time, verifiable by the maintainer against the platform's attestation infrastructure. Tagged union over the discriminator `kind`.\",\n \"oneOf\": [\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"aaguid\": {\n \"description\": \"WebAuthn Authenticator AAGUID (UUID).\",\n \"pattern\": \"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$\",\n \"type\": \"string\"\n },\n \"attestationStatement\": {\n \"description\": \"Base64url-encoded WebAuthn attestation statement, when supplied by the platform.\",\n \"type\": \"string\"\n },\n \"kind\": {\n \"const\": \"webauthn\"\n }\n },\n \"required\": [\n \"kind\",\n \"aaguid\"\n ],\n \"title\": \"WebAuthnAttestation\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"attestation\": {\n \"type\": \"string\"\n },\n \"keyId\": {\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"kind\": {\n \"const\": \"apple-app-attest\"\n }\n },\n \"required\": [\n \"kind\",\n \"keyId\",\n \"attestation\"\n ],\n \"title\": \"AppleAppAttest\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"kind\": {\n \"const\": \"play-integrity\"\n },\n \"token\": {\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"kind\",\n \"token\"\n ],\n \"title\": \"PlayIntegrity\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"kind\": {\n \"const\": \"tpm\"\n },\n \"quote\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"kind\",\n \"quote\"\n ],\n \"title\": \"Tpm\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"properties\": {\n \"kind\": {\n \"const\": \"nitro-enclave\"\n },\n \"quote\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"kind\",\n \"quote\"\n ],\n \"title\": \"NitroEnclave\",\n \"type\": \"object\"\n },\n {\n \"additionalProperties\": false,\n \"description\": \"No device-level attestation is available. Maintainers MAY still register the device but SHOULD apply stricter policy (shorter session TTL, more frequent step-up).\",\n \"properties\": {\n \"kind\": {\n \"const\": \"none\"\n }\n },\n \"required\": [\n \"kind\"\n ],\n \"title\": \"NoAttestation\",\n \"type\": \"object\"\n }\n ],\n \"title\": \"DeviceAttestation\"\n },\n \"DeviceBinding\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"attestation\": {\n \"$ref\": \"#/$defs/DeviceAttestation\"\n },\n \"capabilities\": {\n \"description\": \"Capability bitset granted to this device (mirrors the ACL-side scope). Returned for inspection; mutated only via acl/change-role or device/disable.\",\n \"items\": {\n \"$ref\": \"#/$defs/Capability\"\n },\n \"type\": \"array\",\n \"uniqueItems\": true\n },\n \"consumerDid\": {\n \"description\": \"The long-term VTA-derived key (DID) the device authenticates with. Established via the ACL-swap pattern at registration.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"consumerKind\": {\n \"$ref\": \"#/$defs/ConsumerKind\"\n },\n \"deviceId\": {\n \"description\": \"Maintainer-assigned opaque id for this device. Stable across the device's lifetime — never re-used after disable or wipe.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"disabledAt\": {\n \"description\": \"Present when the device has been disabled (device/disable). Disabled devices cannot authenticate but their record is retained for audit.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"displayName\": {\n \"description\": \"Human-readable name (e.g. \\\"Glenn's MacBook — Chrome\\\", \\\"iPhone 17\\\").\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"lastSeenAt\": {\n \"description\": \"Updated on every device/heartbeat and on any successful auth.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"platform\": {\n \"description\": \"Free-form platform descriptor (e.g. \\\"macOS 16 / Chrome 142\\\", \\\"iOS 19.1\\\", \\\"Android 16\\\", \\\"Linux/x86_64\\\"). Producer-supplied at registration; consumer-supplied updates are accepted on heartbeat.\",\n \"type\": \"string\"\n },\n \"pushCapable\": {\n \"description\": \"Whether this device has registered a push channel with its mediator via the push binding's `set-device-info` (https://trusttasks.org/binding/push/0.1). Informational visibility for device/list only — the actual push token is held by the mediator, never by the maintainer/VTA. The maintainer sets this from a signal supplied by its mediator, not from device-supplied registration data.\",\n \"type\": \"boolean\"\n },\n \"registeredAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"wipedAt\": {\n \"description\": \"Present when a wipe has been issued (device/wipe). Distinct from disabledAt — a wiped device is also disabled, but wipe additionally communicates a wipe-cache instruction that the device may or may not have executed (see device/wipe).\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"deviceId\",\n \"consumerKind\",\n \"displayName\",\n \"registeredAt\",\n \"consumerDid\"\n ],\n \"title\": \"DeviceBinding\",\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 \"properties\": {\n \"binding\": {\n \"$ref\": \"#/$defs/DeviceBinding\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n }\n },\n \"required\": [\n \"binding\"\n ],\n \"title\": \"Device Register — response payload\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/device/register/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"Public discovery surface that wraps the maintainer's existing two-phase enrolment (provision-integration → acl/swap-key). A new Companion or Service hands the maintainer its long-term VTA-derived key, its consumer kind, display name, and an optional device attestation; the maintainer creates the DeviceBinding and returns it. Phase 1 (provision-integration) is assumed to have already happened — this task is the post-bootstrap claim step.\",\n \"properties\": {\n \"attestation\": {\n \"$ref\": \"#/$defs/DeviceAttestation\"\n },\n \"consumerKind\": {\n \"$ref\": \"#/$defs/ConsumerKind\"\n },\n \"displayName\": {\n \"maxLength\": 128,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"hpkePublicKey\": {\n \"description\": \"X25519 public key (did:key form) the maintainer will use to HPKE-seal sensitive payloads to this device (sealed secrets, session blobs, sync events). REQUIRED — every Companion/Service needs a recipient key.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"platform\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"consumerKind\",\n \"displayName\"\n ],\n \"title\": \"Device Register — payload\",\n \"type\": \"object\"\n}\n";
}