//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `git-ns/right/break-glass`. 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())
}
}
}
///How a self-granted right came to be, and whether another administrator has since ratified it. A record whose `breakGlass` has no `ratifiedBy` is **unratified**: it is live and published like any other right, it does not count toward the last-owner or last-admin invariants, and any community administrator or namespace admin of its namespace may revoke it. Ratification (git-ns/right/ratify) sets `ratifiedBy` and `ratifiedAt`; from then on the record is an ordinary grant, and `breakGlass` stays as its history. Never published to the Trust Registry.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "BreakGlass",
/// "description": "How a self-granted right came to be, and whether another administrator has since ratified it. A record whose `breakGlass` has no `ratifiedBy` is **unratified**: it is live and published like any other right, it does not count toward the last-owner or last-admin invariants, and any community administrator or namespace admin of its namespace may revoke it. Ratification (git-ns/right/ratify) sets `ratifiedBy` and `ratifiedAt`; from then on the record is an ordinary grant, and `breakGlass` stays as its history. Never published to the Trust Registry.",
/// "type": "object",
/// "required": [
/// "at",
/// "by",
/// "justification"
/// ],
/// "properties": {
/// "at": {
/// "description": "When the VTC recorded the break-glass.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "by": {
/// "description": "Who broke the glass: always the record's `subject`, restated so the flag reads on its own.",
/// "$ref": "#/definitions/Did"
/// },
/// "effectiveAt": {
/// "description": "When the right takes effect, where the community's policy imposed a delay. Absent: it took effect at `at`. Until this instant the record confers nothing and is not published, and any administrator who may revoke it may do so.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "justification": {
/// "description": "The actor's statement of why nobody else could grant this right. Shown to every community administrator, every namespace admin of the namespace and every owner of the resource; kept in the audit record; never published.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1,
/// "pattern": "\\S"
/// },
/// "ratifiedAt": {
/// "description": "When it was ratified. Present exactly when `ratifiedBy` is.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "ratifiedBy": {
/// "description": "The administrator who ratified the record — never its subject. Absent while unratified.",
/// "$ref": "#/definitions/Did"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct BreakGlass {
///When the VTC recorded the break-glass.
pub at: ::chrono::DateTime<::chrono::offset::Utc>,
///Who broke the glass: always the record's `subject`, restated so the flag reads on its own.
pub by: Did,
///When the right takes effect, where the community's policy imposed a delay. Absent: it took effect at `at`. Until this instant the record confers nothing and is not published, and any administrator who may revoke it may do so.
#[serde(
rename = "effectiveAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub effective_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///The actor's statement of why nobody else could grant this right. Shown to every community administrator, every namespace admin of the namespace and every owner of the resource; kept in the audit record; never published.
pub justification: BreakGlassJustification,
///When it was ratified. Present exactly when `ratifiedBy` is.
#[serde(
rename = "ratifiedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub ratified_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///The administrator who ratified the record — never its subject. Absent while unratified.
#[serde(
rename = "ratifiedBy",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub ratified_by: ::std::option::Option<Did>,
}
impl BreakGlass {
pub fn builder() -> builder::BreakGlass {
Default::default()
}
}
///The actor's statement of why nobody else could grant this right. Shown to every community administrator, every namespace admin of the namespace and every owner of the resource; kept in the audit record; never published.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The actor's statement of why nobody else could grant this right. Shown to every community administrator, every namespace admin of the namespace and every owner of the resource; kept in the audit record; never published.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1,
/// "pattern": "\\S"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct BreakGlassJustification(::std::string::String);
impl ::std::ops::Deref for BreakGlassJustification {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<BreakGlassJustification> for ::std::string::String {
fn from(value: BreakGlassJustification) -> Self {
value.0
}
}
impl ::std::str::FromStr for BreakGlassJustification {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
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("\\S").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"\\S\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for BreakGlassJustification {
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 BreakGlassJustification {
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 BreakGlassJustification {
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 BreakGlassJustification {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A bare DID in the W3C DID Core syntax (§3.1): `did:`, a method name of lowercase letters and digits, `:`, and a method-specific id of colon-separated segments drawn from `A-Z a-z 0-9 . - _` and percent-encoded octets, the last segment non-empty. A DID URL is not a DID: no path, query or fragment (`/`, `?`, `#`), so a verification-method id such as `did:key:z6Mk…#z6Mk…` is refused. Compared by exact string equality — no case folding or percent-decoding. A consumer MUST still treat the value as data: the pattern keeps shell metacharacters, whitespace and quotes out of the wire form, but it does not make a DID safe to splice into a command or markup.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Did",
/// "description": "A bare DID in the W3C DID Core syntax (§3.1): `did:`, a method name of lowercase letters and digits, `:`, and a method-specific id of colon-separated segments drawn from `A-Z a-z 0-9 . - _` and percent-encoded octets, the last segment non-empty. A DID URL is not a DID: no path, query or fragment (`/`, `?`, `#`), so a verification-method id such as `did:key:z6Mk…#z6Mk…` is refused. Compared by exact string equality — no case folding or percent-decoding. A consumer MUST still treat the value as data: the pattern keeps shell metacharacters, whitespace and quotes out of the wire form, but it does not make a DID safe to splice into a command or markup.",
/// "type": "string",
/// "maxLength": 2048,
/// "pattern": "^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Did(::std::string::String);
impl ::std::ops::Deref for Did {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Did> for ::std::string::String {
fn from(value: Did) -> Self {
value.0
}
}
impl ::std::str::FromStr for Did {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(
|| {
::regress::Regex::new(
"^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$",
)
.unwrap()
},
);
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Did {
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 Did {
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 Did {
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 Did {
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 three rights that carry authority over other people's rights: `git.ns.admin`, `git.repo.create` (which makes its holder the owner of every repository they create) and `git.repo.own`. Separation of duties applies to these: nobody grants one to themselves through git-ns/right/grant, or records one for themselves through any other task that grants on the actor's own authority. The explicit self-grant is git-ns/right/break-glass. `git.repo.maintain` and `git.commit.sign` are not elevated.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ElevatedRight",
/// "description": "The three rights that carry authority over other people's rights: `git.ns.admin`, `git.repo.create` (which makes its holder the owner of every repository they create) and `git.repo.own`. Separation of duties applies to these: nobody grants one to themselves through git-ns/right/grant, or records one for themselves through any other task that grants on the actor's own authority. The explicit self-grant is git-ns/right/break-glass. `git.repo.maintain` and `git.commit.sign` are not elevated.",
/// "type": "string",
/// "enum": [
/// "git.ns.admin",
/// "git.repo.create",
/// "git.repo.own"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum ElevatedRight {
#[serde(rename = "git.ns.admin")]
GitNsAdmin,
#[serde(rename = "git.repo.create")]
GitRepoCreate,
#[serde(rename = "git.repo.own")]
GitRepoOwn,
}
impl ::std::fmt::Display for ElevatedRight {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::GitNsAdmin => f.write_str("git.ns.admin"),
Self::GitRepoCreate => f.write_str("git.repo.create"),
Self::GitRepoOwn => f.write_str("git.repo.own"),
}
}
}
impl ::std::str::FromStr for ElevatedRight {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"git.ns.admin" => Ok(Self::GitNsAdmin),
"git.repo.create" => Ok(Self::GitRepoCreate),
"git.repo.own" => Ok(Self::GitRepoOwn),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ElevatedRight {
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 ElevatedRight {
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 ElevatedRight {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///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())
})
}
}
///The actor records an elevated git right for themselves — one they already have the authority to grant, but which separation of duties forbids them granting to themselves through git-ns/right/grant — with a mandatory justification. The right takes effect at once, never expires on its own, and is flagged as break-glass, and shown to every other administrator, until another administrator ratifies it with git-ns/right/ratify or anyone entitled revokes it.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/git-ns/right/break-glass/0.1",
/// "title": "Payload",
/// "description": "The actor records an elevated git right for themselves — one they already have the authority to grant, but which separation of duties forbids them granting to themselves through git-ns/right/grant — with a mandatory justification. The right takes effect at once, never expires on its own, and is flagged as break-glass, and shown to every other administrator, until another administrator ratifies it with git-ns/right/ratify or anyone entitled revokes it.",
/// "type": "object",
/// "required": [
/// "justification",
/// "resource",
/// "right"
/// ],
/// "properties": {
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "justification": {
/// "description": "Why nobody else could grant this right now, in the actor's words. Required and never blank. Recorded with the right as `breakGlass.justification`, carried in the audit record and in every notice, and shown to every community administrator, every namespace admin of the namespace and every owner of the resource. Never published to the Trust Registry.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1,
/// "pattern": "\\S"
/// },
/// "resource": {
/// "description": "Where the right applies: the namespace resource for `git.ns.admin` and `git.repo.create`, a repository resource for `git.repo.own`.",
/// "$ref": "#/definitions/Resource"
/// },
/// "right": {
/// "description": "The elevated right the actor records for themselves. The other two rights need no break-glass: git-ns/right/grant lets anyone with authority over them grant them to themselves.",
/// "$ref": "#/definitions/ElevatedRight"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Why nobody else could grant this right now, in the actor's words. Required and never blank. Recorded with the right as `breakGlass.justification`, carried in the audit record and in every notice, and shown to every community administrator, every namespace admin of the namespace and every owner of the resource. Never published to the Trust Registry.
pub justification: PayloadJustification,
///Where the right applies: the namespace resource for `git.ns.admin` and `git.repo.create`, a repository resource for `git.repo.own`.
pub resource: Resource,
///The elevated right the actor records for themselves. The other two rights need no break-glass: git-ns/right/grant lets anyone with authority over them grant them to themselves.
pub right: ElevatedRight,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///Why nobody else could grant this right now, in the actor's words. Required and never blank. Recorded with the right as `breakGlass.justification`, carried in the audit record and in every notice, and shown to every community administrator, every namespace admin of the namespace and every owner of the resource. Never published to the Trust Registry.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Why nobody else could grant this right now, in the actor's words. Required and never blank. Recorded with the right as `breakGlass.justification`, carried in the audit record and in every notice, and shown to every community administrator, every namespace admin of the namespace and every owner of the resource. Never published to the Trust Registry.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1,
/// "pattern": "\\S"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadJustification(::std::string::String);
impl ::std::ops::Deref for PayloadJustification {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadJustification> for ::std::string::String {
fn from(value: PayloadJustification) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadJustification {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 2048usize {
return Err("longer than 2048 characters".into());
}
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("\\S").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"\\S\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadJustification {
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 PayloadJustification {
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 PayloadJustification {
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 PayloadJustification {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Resource",
/// "description": "A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.",
/// "type": "string",
/// "maxLength": 455,
/// "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Resource(::std::string::String);
impl ::std::ops::Deref for Resource {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Resource> for ::std::string::String {
fn from(value: Resource) -> Self {
value.0
}
}
impl ::std::str::FromStr for Resource {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 455usize {
return Err("longer than 455 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(
|| {
::regress::Regex::new(
"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$",
)
.unwrap()
},
);
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Resource {
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 Resource {
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 Resource {
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 Resource {
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 recorded right, carrying its `breakGlass` flag.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "The recorded right, carrying its `breakGlass` flag.",
/// "type": "object",
/// "required": [
/// "right"
/// ],
/// "properties": {
/// "ext": {
/// "$ref": "#/definitions/Ext"
/// },
/// "right": {
/// "description": "The recorded right — newly recorded with `breakGlass`, or the live record the actor already held, returned unchanged.",
/// "$ref": "#/definitions/RightRecord"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///The recorded right — newly recorded with `breakGlass`, or the live record the actor already held, returned unchanged.
pub right: RightRecord,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///One of the five git rights. Each string is also the TRQP `action` the VTC publishes the right under in its Trust Registry, so it is carried verbatim. `git.ns.admin` and `git.repo.create` apply to a namespace resource; `git.repo.own` and `git.repo.maintain` to a repository resource; `git.commit.sign` to either.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Right",
/// "description": "One of the five git rights. Each string is also the TRQP `action` the VTC publishes the right under in its Trust Registry, so it is carried verbatim. `git.ns.admin` and `git.repo.create` apply to a namespace resource; `git.repo.own` and `git.repo.maintain` to a repository resource; `git.commit.sign` to either.",
/// "type": "string",
/// "enum": [
/// "git.ns.admin",
/// "git.repo.create",
/// "git.repo.own",
/// "git.repo.maintain",
/// "git.commit.sign"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum Right {
#[serde(rename = "git.ns.admin")]
GitNsAdmin,
#[serde(rename = "git.repo.create")]
GitRepoCreate,
#[serde(rename = "git.repo.own")]
GitRepoOwn,
#[serde(rename = "git.repo.maintain")]
GitRepoMaintain,
#[serde(rename = "git.commit.sign")]
GitCommitSign,
}
impl ::std::fmt::Display for Right {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::GitNsAdmin => f.write_str("git.ns.admin"),
Self::GitRepoCreate => f.write_str("git.repo.create"),
Self::GitRepoOwn => f.write_str("git.repo.own"),
Self::GitRepoMaintain => f.write_str("git.repo.maintain"),
Self::GitCommitSign => f.write_str("git.commit.sign"),
}
}
}
impl ::std::str::FromStr for Right {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"git.ns.admin" => Ok(Self::GitNsAdmin),
"git.repo.create" => Ok(Self::GitRepoCreate),
"git.repo.own" => Ok(Self::GitRepoOwn),
"git.repo.maintain" => Ok(Self::GitRepoMaintain),
"git.commit.sign" => Ok(Self::GitCommitSign),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Right {
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 Right {
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 Right {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///One recorded right. Implied rights (§4.2 of the rights model: `own` implies `maintain` implies `commit.sign` on the same resource; `ns.admin` implies `repo.create` and `own` across its namespace) are not records and never appear as RightRecords. A record carrying `breakGlass` was given by its subject to themselves through git-ns/right/break-glass; it is a real right, published like any other, and is shown with that flag on every surface that shows the record.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "RightRecord",
/// "description": "One recorded right. Implied rights (§4.2 of the rights model: `own` implies `maintain` implies `commit.sign` on the same resource; `ns.admin` implies `repo.create` and `own` across its namespace) are not records and never appear as RightRecords. A record carrying `breakGlass` was given by its subject to themselves through git-ns/right/break-glass; it is a real right, published like any other, and is shown with that flag on every surface that shows the record.",
/// "type": "object",
/// "required": [
/// "grantedAt",
/// "grantedBy",
/// "resource",
/// "right",
/// "subject"
/// ],
/// "properties": {
/// "breakGlass": {
/// "description": "Present exactly when the subject gave themselves this right through git-ns/right/break-glass. Absent for every other record.",
/// "$ref": "#/definitions/BreakGlass"
/// },
/// "expiresAt": {
/// "description": "When the right lapses. Absent: no expiry.",
/// "type": "string",
/// "format": "date-time"
/// },
/// "grantedAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "grantedBy": {
/// "description": "The actor whose task caused the right: the granter, the creator of a repository (for its first `own`), the adopting admin, the transferring owner, or the binding admin (for the first `git.ns.admin`). The VTC's own DID for a right it derives from its configuration.",
/// "$ref": "#/definitions/Did"
/// },
/// "reason": {
/// "description": "The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.",
/// "type": "string",
/// "maxLength": 1024
/// },
/// "resource": {
/// "$ref": "#/definitions/Resource"
/// },
/// "right": {
/// "$ref": "#/definitions/Right"
/// },
/// "subject": {
/// "description": "Who holds the right. For `git.commit.sign` this is the DID whose commit signatures the CI check accepts.",
/// "$ref": "#/definitions/Did"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct RightRecord {
///Present exactly when the subject gave themselves this right through git-ns/right/break-glass. Absent for every other record.
#[serde(
rename = "breakGlass",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub break_glass: ::std::option::Option<BreakGlass>,
///When the right lapses. Absent: no expiry.
#[serde(
rename = "expiresAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub expires_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
#[serde(rename = "grantedAt")]
pub granted_at: ::chrono::DateTime<::chrono::offset::Utc>,
///The actor whose task caused the right: the granter, the creator of a repository (for its first `own`), the adopting admin, the transferring owner, or the binding admin (for the first `git.ns.admin`). The VTC's own DID for a right it derives from its configuration.
#[serde(rename = "grantedBy")]
pub granted_by: Did,
///The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub reason: ::std::option::Option<RightRecordReason>,
pub resource: Resource,
pub right: Right,
///Who holds the right. For `git.commit.sign` this is the DID whose commit signatures the CI check accepts.
pub subject: Did,
}
impl RightRecord {
pub fn builder() -> builder::RightRecord {
Default::default()
}
}
///The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.",
/// "type": "string",
/// "maxLength": 1024
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct RightRecordReason(::std::string::String);
impl ::std::ops::Deref for RightRecordReason {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<RightRecordReason> for ::std::string::String {
fn from(value: RightRecordReason) -> Self {
value.0
}
}
impl ::std::str::FromStr for RightRecordReason {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 1024usize {
return Err("longer than 1024 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for RightRecordReason {
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 RightRecordReason {
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 RightRecordReason {
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 RightRecordReason {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct BreakGlass {
at: ::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
by: ::std::result::Result<super::Did, ::std::string::String>,
effective_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
justification: ::std::result::Result<super::BreakGlassJustification, ::std::string::String>,
ratified_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
ratified_by:
::std::result::Result<::std::option::Option<super::Did>, ::std::string::String>,
}
impl ::std::default::Default for BreakGlass {
fn default() -> Self {
Self {
at: Err("no value supplied for at".to_string()),
by: Err("no value supplied for by".to_string()),
effective_at: Ok(Default::default()),
justification: Err("no value supplied for justification".to_string()),
ratified_at: Ok(Default::default()),
ratified_by: Ok(Default::default()),
}
}
}
impl BreakGlass {
pub fn at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.at = value
.try_into()
.map_err(|e| format!("error converting supplied value for at: {e}"));
self
}
pub fn by<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Did>,
T::Error: ::std::fmt::Display,
{
self.by = value
.try_into()
.map_err(|e| format!("error converting supplied value for by: {e}"));
self
}
pub fn effective_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.effective_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for effective_at: {e}"));
self
}
pub fn justification<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::BreakGlassJustification>,
T::Error: ::std::fmt::Display,
{
self.justification = value
.try_into()
.map_err(|e| format!("error converting supplied value for justification: {e}"));
self
}
pub fn ratified_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.ratified_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for ratified_at: {e}"));
self
}
pub fn ratified_by<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Did>>,
T::Error: ::std::fmt::Display,
{
self.ratified_by = value
.try_into()
.map_err(|e| format!("error converting supplied value for ratified_by: {e}"));
self
}
}
impl ::std::convert::TryFrom<BreakGlass> for super::BreakGlass {
type Error = super::error::ConversionError;
fn try_from(
value: BreakGlass,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
at: value.at?,
by: value.by?,
effective_at: value.effective_at?,
justification: value.justification?,
ratified_at: value.ratified_at?,
ratified_by: value.ratified_by?,
})
}
}
impl ::std::convert::From<super::BreakGlass> for BreakGlass {
fn from(value: super::BreakGlass) -> Self {
Self {
at: Ok(value.at),
by: Ok(value.by),
effective_at: Ok(value.effective_at),
justification: Ok(value.justification),
ratified_at: Ok(value.ratified_at),
ratified_by: Ok(value.ratified_by),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
justification: ::std::result::Result<super::PayloadJustification, ::std::string::String>,
resource: ::std::result::Result<super::Resource, ::std::string::String>,
right: ::std::result::Result<super::ElevatedRight, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
ext: Ok(Default::default()),
justification: Err("no value supplied for justification".to_string()),
resource: Err("no value supplied for resource".to_string()),
right: Err("no value supplied for right".to_string()),
}
}
}
impl Payload {
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn justification<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadJustification>,
T::Error: ::std::fmt::Display,
{
self.justification = value
.try_into()
.map_err(|e| format!("error converting supplied value for justification: {e}"));
self
}
pub fn resource<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Resource>,
T::Error: ::std::fmt::Display,
{
self.resource = value
.try_into()
.map_err(|e| format!("error converting supplied value for resource: {e}"));
self
}
pub fn right<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ElevatedRight>,
T::Error: ::std::fmt::Display,
{
self.right = value
.try_into()
.map_err(|e| format!("error converting supplied value for right: {e}"));
self
}
}
impl ::std::convert::TryFrom<Payload> for super::Payload {
type Error = super::error::ConversionError;
fn try_from(value: Payload) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
ext: value.ext?,
justification: value.justification?,
resource: value.resource?,
right: value.right?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
ext: Ok(value.ext),
justification: Ok(value.justification),
resource: Ok(value.resource),
right: Ok(value.right),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
right: ::std::result::Result<super::RightRecord, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
ext: Ok(Default::default()),
right: Err("no value supplied for right".to_string()),
}
}
}
impl Response {
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn right<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::RightRecord>,
T::Error: ::std::fmt::Display,
{
self.right = value
.try_into()
.map_err(|e| format!("error converting supplied value for right: {e}"));
self
}
}
impl ::std::convert::TryFrom<Response> for super::Response {
type Error = super::error::ConversionError;
fn try_from(value: Response) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
ext: value.ext?,
right: value.right?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
ext: Ok(value.ext),
right: Ok(value.right),
}
}
}
#[derive(Clone, Debug)]
pub struct RightRecord {
break_glass:
::std::result::Result<::std::option::Option<super::BreakGlass>, ::std::string::String>,
expires_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
granted_at:
::std::result::Result<::chrono::DateTime<::chrono::offset::Utc>, ::std::string::String>,
granted_by: ::std::result::Result<super::Did, ::std::string::String>,
reason: ::std::result::Result<
::std::option::Option<super::RightRecordReason>,
::std::string::String,
>,
resource: ::std::result::Result<super::Resource, ::std::string::String>,
right: ::std::result::Result<super::Right, ::std::string::String>,
subject: ::std::result::Result<super::Did, ::std::string::String>,
}
impl ::std::default::Default for RightRecord {
fn default() -> Self {
Self {
break_glass: Ok(Default::default()),
expires_at: Ok(Default::default()),
granted_at: Err("no value supplied for granted_at".to_string()),
granted_by: Err("no value supplied for granted_by".to_string()),
reason: Ok(Default::default()),
resource: Err("no value supplied for resource".to_string()),
right: Err("no value supplied for right".to_string()),
subject: Err("no value supplied for subject".to_string()),
}
}
}
impl RightRecord {
pub fn break_glass<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::BreakGlass>>,
T::Error: ::std::fmt::Display,
{
self.break_glass = value
.try_into()
.map_err(|e| format!("error converting supplied value for break_glass: {e}"));
self
}
pub fn expires_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
>,
T::Error: ::std::fmt::Display,
{
self.expires_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for expires_at: {e}"));
self
}
pub fn granted_at<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::chrono::DateTime<::chrono::offset::Utc>>,
T::Error: ::std::fmt::Display,
{
self.granted_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for granted_at: {e}"));
self
}
pub fn granted_by<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Did>,
T::Error: ::std::fmt::Display,
{
self.granted_by = value
.try_into()
.map_err(|e| format!("error converting supplied value for granted_by: {e}"));
self
}
pub fn reason<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RightRecordReason>>,
T::Error: ::std::fmt::Display,
{
self.reason = value
.try_into()
.map_err(|e| format!("error converting supplied value for reason: {e}"));
self
}
pub fn resource<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Resource>,
T::Error: ::std::fmt::Display,
{
self.resource = value
.try_into()
.map_err(|e| format!("error converting supplied value for resource: {e}"));
self
}
pub fn right<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Right>,
T::Error: ::std::fmt::Display,
{
self.right = value
.try_into()
.map_err(|e| format!("error converting supplied value for right: {e}"));
self
}
pub fn subject<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Did>,
T::Error: ::std::fmt::Display,
{
self.subject = value
.try_into()
.map_err(|e| format!("error converting supplied value for subject: {e}"));
self
}
}
impl ::std::convert::TryFrom<RightRecord> for super::RightRecord {
type Error = super::error::ConversionError;
fn try_from(
value: RightRecord,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
break_glass: value.break_glass?,
expires_at: value.expires_at?,
granted_at: value.granted_at?,
granted_by: value.granted_by?,
reason: value.reason?,
resource: value.resource?,
right: value.right?,
subject: value.subject?,
})
}
}
impl ::std::convert::From<super::RightRecord> for RightRecord {
fn from(value: super::RightRecord) -> Self {
Self {
break_glass: Ok(value.break_glass),
expires_at: Ok(value.expires_at),
granted_at: Ok(value.granted_at),
granted_by: Ok(value.granted_by),
reason: Ok(value.reason),
resource: Ok(value.resource),
right: Ok(value.right),
subject: Ok(value.subject),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/git-ns/right/break-glass/0.1";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"BreakGlass\": {\n \"additionalProperties\": false,\n \"description\": \"How a self-granted right came to be, and whether another administrator has since ratified it. A record whose `breakGlass` has no `ratifiedBy` is **unratified**: it is live and published like any other right, it does not count toward the last-owner or last-admin invariants, and any community administrator or namespace admin of its namespace may revoke it. Ratification (git-ns/right/ratify) sets `ratifiedBy` and `ratifiedAt`; from then on the record is an ordinary grant, and `breakGlass` stays as its history. Never published to the Trust Registry.\",\n \"properties\": {\n \"at\": {\n \"description\": \"When the VTC recorded the break-glass.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"by\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"Who broke the glass: always the record's `subject`, restated so the flag reads on its own.\"\n },\n \"effectiveAt\": {\n \"description\": \"When the right takes effect, where the community's policy imposed a delay. Absent: it took effect at `at`. Until this instant the record confers nothing and is not published, and any administrator who may revoke it may do so.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"justification\": {\n \"description\": \"The actor's statement of why nobody else could grant this right. Shown to every community administrator, every namespace admin of the namespace and every owner of the resource; kept in the audit record; never published.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"pattern\": \"\\\\S\",\n \"type\": \"string\"\n },\n \"ratifiedAt\": {\n \"description\": \"When it was ratified. Present exactly when `ratifiedBy` is.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"ratifiedBy\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The administrator who ratified the record — never its subject. Absent while unratified.\"\n }\n },\n \"required\": [\n \"by\",\n \"at\",\n \"justification\"\n ],\n \"title\": \"BreakGlass\",\n \"type\": \"object\"\n },\n \"Did\": {\n \"description\": \"A bare DID in the W3C DID Core syntax (§3.1): `did:`, a method name of lowercase letters and digits, `:`, and a method-specific id of colon-separated segments drawn from `A-Z a-z 0-9 . - _` and percent-encoded octets, the last segment non-empty. A DID URL is not a DID: no path, query or fragment (`/`, `?`, `#`), so a verification-method id such as `did:key:z6Mk…#z6Mk…` is refused. Compared by exact string equality — no case folding or percent-decoding. A consumer MUST still treat the value as data: the pattern keeps shell metacharacters, whitespace and quotes out of the wire form, but it does not make a DID safe to splice into a command or markup.\",\n \"maxLength\": 2048,\n \"pattern\": \"^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$\",\n \"title\": \"Did\",\n \"type\": \"string\"\n },\n \"ElevatedRight\": {\n \"description\": \"The three rights that carry authority over other people's rights: `git.ns.admin`, `git.repo.create` (which makes its holder the owner of every repository they create) and `git.repo.own`. Separation of duties applies to these: nobody grants one to themselves through git-ns/right/grant, or records one for themselves through any other task that grants on the actor's own authority. The explicit self-grant is git-ns/right/break-glass. `git.repo.maintain` and `git.commit.sign` are not elevated.\",\n \"enum\": [\n \"git.ns.admin\",\n \"git.repo.create\",\n \"git.repo.own\"\n ],\n \"title\": \"ElevatedRight\",\n \"type\": \"string\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Resource\": {\n \"description\": \"A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.\",\n \"maxLength\": 455,\n \"pattern\": \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\",\n \"title\": \"Resource\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"The recorded right, carrying its `breakGlass` flag.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/RightRecord\",\n \"description\": \"The recorded right — newly recorded with `breakGlass`, or the live record the actor already held, returned unchanged.\"\n }\n },\n \"required\": [\n \"right\"\n ],\n \"title\": \"Git Namespaces — Break Glass — response payload\",\n \"type\": \"object\"\n },\n \"Right\": {\n \"description\": \"One of the five git rights. Each string is also the TRQP `action` the VTC publishes the right under in its Trust Registry, so it is carried verbatim. `git.ns.admin` and `git.repo.create` apply to a namespace resource; `git.repo.own` and `git.repo.maintain` to a repository resource; `git.commit.sign` to either.\",\n \"enum\": [\n \"git.ns.admin\",\n \"git.repo.create\",\n \"git.repo.own\",\n \"git.repo.maintain\",\n \"git.commit.sign\"\n ],\n \"title\": \"Right\",\n \"type\": \"string\"\n },\n \"RightRecord\": {\n \"additionalProperties\": false,\n \"description\": \"One recorded right. Implied rights (§4.2 of the rights model: `own` implies `maintain` implies `commit.sign` on the same resource; `ns.admin` implies `repo.create` and `own` across its namespace) are not records and never appear as RightRecords. A record carrying `breakGlass` was given by its subject to themselves through git-ns/right/break-glass; it is a real right, published like any other, and is shown with that flag on every surface that shows the record.\",\n \"properties\": {\n \"breakGlass\": {\n \"$ref\": \"#/$defs/BreakGlass\",\n \"description\": \"Present exactly when the subject gave themselves this right through git-ns/right/break-glass. Absent for every other record.\"\n },\n \"expiresAt\": {\n \"description\": \"When the right lapses. Absent: no expiry.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"grantedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"grantedBy\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The actor whose task caused the right: the granter, the creator of a repository (for its first `own`), the adopting admin, the transferring owner, or the binding admin (for the first `git.ns.admin`). The VTC's own DID for a right it derives from its configuration.\"\n },\n \"reason\": {\n \"description\": \"The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/Resource\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/Right\"\n },\n \"subject\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"Who holds the right. For `git.commit.sign` this is the DID whose commit signatures the CI check accepts.\"\n }\n },\n \"required\": [\n \"subject\",\n \"right\",\n \"resource\",\n \"grantedBy\",\n \"grantedAt\"\n ],\n \"title\": \"RightRecord\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/git-ns/right/break-glass/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"The actor records an elevated git right for themselves — one they already have the authority to grant, but which separation of duties forbids them granting to themselves through git-ns/right/grant — with a mandatory justification. The right takes effect at once, never expires on its own, and is flagged as break-glass, and shown to every other administrator, until another administrator ratifies it with git-ns/right/ratify or anyone entitled revokes it.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"justification\": {\n \"description\": \"Why nobody else could grant this right now, in the actor's words. Required and never blank. Recorded with the right as `breakGlass.justification`, carried in the audit record and in every notice, and shown to every community administrator, every namespace admin of the namespace and every owner of the resource. Never published to the Trust Registry.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"pattern\": \"\\\\S\",\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/Resource\",\n \"description\": \"Where the right applies: the namespace resource for `git.ns.admin` and `git.repo.create`, a repository resource for `git.repo.own`.\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/ElevatedRight\",\n \"description\": \"The elevated right the actor records for themselves. The other two rights need no break-glass: git-ns/right/grant lets anyone with authority over them grant them to themselves.\"\n }\n },\n \"required\": [\n \"right\",\n \"resource\",\n \"justification\"\n ],\n \"title\": \"Git Namespaces — Break Glass — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/git-ns/right/break-glass/0.1#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_ISSUED_AT_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"BreakGlass\": {\n \"additionalProperties\": false,\n \"description\": \"How a self-granted right came to be, and whether another administrator has since ratified it. A record whose `breakGlass` has no `ratifiedBy` is **unratified**: it is live and published like any other right, it does not count toward the last-owner or last-admin invariants, and any community administrator or namespace admin of its namespace may revoke it. Ratification (git-ns/right/ratify) sets `ratifiedBy` and `ratifiedAt`; from then on the record is an ordinary grant, and `breakGlass` stays as its history. Never published to the Trust Registry.\",\n \"properties\": {\n \"at\": {\n \"description\": \"When the VTC recorded the break-glass.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"by\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"Who broke the glass: always the record's `subject`, restated so the flag reads on its own.\"\n },\n \"effectiveAt\": {\n \"description\": \"When the right takes effect, where the community's policy imposed a delay. Absent: it took effect at `at`. Until this instant the record confers nothing and is not published, and any administrator who may revoke it may do so.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"justification\": {\n \"description\": \"The actor's statement of why nobody else could grant this right. Shown to every community administrator, every namespace admin of the namespace and every owner of the resource; kept in the audit record; never published.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"pattern\": \"\\\\S\",\n \"type\": \"string\"\n },\n \"ratifiedAt\": {\n \"description\": \"When it was ratified. Present exactly when `ratifiedBy` is.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"ratifiedBy\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The administrator who ratified the record — never its subject. Absent while unratified.\"\n }\n },\n \"required\": [\n \"by\",\n \"at\",\n \"justification\"\n ],\n \"title\": \"BreakGlass\",\n \"type\": \"object\"\n },\n \"Did\": {\n \"description\": \"A bare DID in the W3C DID Core syntax (§3.1): `did:`, a method name of lowercase letters and digits, `:`, and a method-specific id of colon-separated segments drawn from `A-Z a-z 0-9 . - _` and percent-encoded octets, the last segment non-empty. A DID URL is not a DID: no path, query or fragment (`/`, `?`, `#`), so a verification-method id such as `did:key:z6Mk…#z6Mk…` is refused. Compared by exact string equality — no case folding or percent-decoding. A consumer MUST still treat the value as data: the pattern keeps shell metacharacters, whitespace and quotes out of the wire form, but it does not make a DID safe to splice into a command or markup.\",\n \"maxLength\": 2048,\n \"pattern\": \"^did:[a-z0-9]+:(?:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})*:)*(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+$\",\n \"title\": \"Did\",\n \"type\": \"string\"\n },\n \"ElevatedRight\": {\n \"description\": \"The three rights that carry authority over other people's rights: `git.ns.admin`, `git.repo.create` (which makes its holder the owner of every repository they create) and `git.repo.own`. Separation of duties applies to these: nobody grants one to themselves through git-ns/right/grant, or records one for themselves through any other task that grants on the actor's own authority. The explicit self-grant is git-ns/right/break-glass. `git.repo.maintain` and `git.commit.sign` are not elevated.\",\n \"enum\": [\n \"git.ns.admin\",\n \"git.repo.create\",\n \"git.repo.own\"\n ],\n \"title\": \"ElevatedRight\",\n \"type\": \"string\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Resource\": {\n \"description\": \"A forge-qualified resource: `<forge-host>/<owner>` for a namespace, or `<forge-host>/<owner>/<repo>` for one repository, all lowercase — `github.com/acme`, `github.com/acme/widgets`, `codeberg.org/acme`. The forge is never implied: `acme/widgets` alone is not a resource. Containment is by whole segment: `github.com/acme` contains `github.com/acme/widgets` and does not contain `github.com/acme-labs/x` or `codeberg.org/acme/widgets`.\",\n \"maxLength\": 455,\n \"pattern\": \"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?:/[a-z0-9_-][a-z0-9._-]{0,99}){1,2}$\",\n \"title\": \"Resource\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"The recorded right, carrying its `breakGlass` flag.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/RightRecord\",\n \"description\": \"The recorded right — newly recorded with `breakGlass`, or the live record the actor already held, returned unchanged.\"\n }\n },\n \"required\": [\n \"right\"\n ],\n \"title\": \"Git Namespaces — Break Glass — response payload\",\n \"type\": \"object\"\n },\n \"Right\": {\n \"description\": \"One of the five git rights. Each string is also the TRQP `action` the VTC publishes the right under in its Trust Registry, so it is carried verbatim. `git.ns.admin` and `git.repo.create` apply to a namespace resource; `git.repo.own` and `git.repo.maintain` to a repository resource; `git.commit.sign` to either.\",\n \"enum\": [\n \"git.ns.admin\",\n \"git.repo.create\",\n \"git.repo.own\",\n \"git.repo.maintain\",\n \"git.commit.sign\"\n ],\n \"title\": \"Right\",\n \"type\": \"string\"\n },\n \"RightRecord\": {\n \"additionalProperties\": false,\n \"description\": \"One recorded right. Implied rights (§4.2 of the rights model: `own` implies `maintain` implies `commit.sign` on the same resource; `ns.admin` implies `repo.create` and `own` across its namespace) are not records and never appear as RightRecords. A record carrying `breakGlass` was given by its subject to themselves through git-ns/right/break-glass; it is a real right, published like any other, and is shown with that flag on every surface that shows the record.\",\n \"properties\": {\n \"breakGlass\": {\n \"$ref\": \"#/$defs/BreakGlass\",\n \"description\": \"Present exactly when the subject gave themselves this right through git-ns/right/break-glass. Absent for every other record.\"\n },\n \"expiresAt\": {\n \"description\": \"When the right lapses. Absent: no expiry.\",\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"grantedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"grantedBy\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"The actor whose task caused the right: the granter, the creator of a repository (for its first `own`), the adopting admin, the transferring owner, or the binding admin (for the first `git.ns.admin`). The VTC's own DID for a right it derives from its configuration.\"\n },\n \"reason\": {\n \"description\": \"The granter's free-text reason. Disclosed only to holders of `git.repo.own` on the resource and of `git.ns.admin` over it.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/Resource\"\n },\n \"right\": {\n \"$ref\": \"#/$defs/Right\"\n },\n \"subject\": {\n \"$ref\": \"#/$defs/Did\",\n \"description\": \"Who holds the right. For `git.commit.sign` this is the DID whose commit signatures the CI check accepts.\"\n }\n },\n \"required\": [\n \"subject\",\n \"right\",\n \"resource\",\n \"grantedBy\",\n \"grantedAt\"\n ],\n \"title\": \"RightRecord\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
/// The extended error codes this specification declares (SPEC §7.3 item 9,
/// §8.5), in declaration order. Empty when it declares none.
pub const ERROR_CODES: &[crate::DeclaredErrorCode] = &[
error_codes::UNKNOWN_NAMESPACE,
error_codes::NAMESPACE_NOT_BOUND,
error_codes::UNKNOWN_REPO,
error_codes::REPO_NOT_ACTIVE,
error_codes::SCOPE_VIOLATION,
error_codes::ESCALATION,
error_codes::MEMBERS_ONLY,
error_codes::POLICY_DENIED,
error_codes::DISABLED,
error_codes::NOT_HEADLESS,
];
/// One constant per extended error code this specification declares
/// (SPEC §7.3 item 9), named for its local part.
///
/// Emit these rather than a string literal: the code is read from the
/// specification, so it cannot name a code the specification never
/// declared.
pub mod error_codes {
/// `git-ns:unknownNamespace`
///
/// No namespace bound to this VTC has this identifier, or contains this resource.
///
/// Declared `retryable: false`.
pub const UNKNOWN_NAMESPACE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:unknownNamespace",
retryable: false,
};
/// `git-ns:namespaceNotBound`
///
/// The namespace is still `pending`. Nothing is created, adopted or granted in it until binding completes.
///
/// Declared `retryable: false`.
pub const NAMESPACE_NOT_BOUND: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:namespaceNotBound",
retryable: false,
};
/// `git-ns:unknownRepo`
///
/// The resource names no repository this VTC records.
///
/// Declared `retryable: false`.
pub const UNKNOWN_REPO: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:unknownRepo",
retryable: false,
};
/// `git-ns:repoNotActive`
///
/// The repository's state does not allow this operation; each task says which states it accepts.
///
/// Declared `retryable: false`.
pub const REPO_NOT_ACTIVE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:repoNotActive",
retryable: false,
};
/// `git-ns:scopeViolation`
///
/// The resource is wider than the actor's own scope, or is the wrong level for the right: a repository right named on a namespace, or a namespace right named on a repository.
///
/// Declared `retryable: false`.
pub const SCOPE_VIOLATION: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:scopeViolation",
retryable: false,
};
/// `git-ns:escalation`
///
/// No right the actor holds on this resource carries the authority to grant or revoke this right.
///
/// Declared `retryable: false`.
pub const ESCALATION: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:escalation",
retryable: false,
};
/// `git-ns:membersOnly`
///
/// An elevated right goes only to a current member of the community holding standing in the VTC's access-control records, and the actor is not one.
///
/// Declared `retryable: false`.
pub const MEMBERS_ONLY: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:membersOnly",
retryable: false,
};
/// `git-ns:policyDenied`
///
/// The community's git-namespace policy refused the request after the fixed rules passed.
///
/// Declared `retryable: false`.
pub const POLICY_DENIED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns:policyDenied",
retryable: false,
};
/// `git-ns/right/break-glass:disabled`
///
/// The community's git-namespace policy does not allow break-glass at all, or not for this right or resource. The actor must find another administrator to grant the right.
///
/// Declared `retryable: false`.
pub const DISABLED: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/right/break-glass:disabled",
retryable: false,
};
/// `git-ns/right/break-glass:notHeadless`
///
/// The actor's only standing is the community-administrator capability, which reaches `git.ns.admin` only on a headless namespace, and this namespace has a live `git.ns.admin`. Its admins grant the right with git-ns/right/grant.
///
/// Declared `retryable: false`.
pub const NOT_HEADLESS: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "git-ns/right/break-glass:notHeadless",
retryable: false,
};
}
#[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 request_example_1() {
const JSON: &str = "{\n \"id\": \"urn:uuid:0b3c9f4e-8a21-4d6b-9e57-3f1a2c4d5e01\",\n \"type\": \"https://trusttasks.org/spec/git-ns/right/break-glass/0.1\",\n \"threadId\": \"urn:uuid:0b3c9f4e-8a21-4d6b-9e57-3f1a2c4d5e01\",\n \"issuer\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"recipient\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"issuedAt\": \"2026-09-25T02:10:00Z\",\n \"payload\": {\n \"right\": \"git.repo.own\",\n \"resource\": \"github.com/acme/widgets\",\n \"justification\": \"CVE-2026-4411 fix must ship tonight; both owners (Alice, Bob) unreachable since 22:00, paged twice. Will ask Dana to ratify in the morning.\"\n },\n \"proof\": {\n \"type\": \"DataIntegrityProof\",\n \"cryptosuite\": \"eddsa-jcs-2022\",\n \"verificationMethod\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol#key-1\",\n \"created\": \"2026-09-25T02:10:00Z\",\n \"proofPurpose\": \"assertionMethod\",\n \"proofValue\": \"z4Hn8Qw2Tb6Rk9Xd3Lc7Mp1Fz5Ya8Js3Vg6Ue2Ti4Ob9Qn7Mk1Cr5Pw8El3Gh6Sy2Ad4Bv9Xf1Lj7Tq5Rz3Kn2M\"\n }\n}\n";
let doc: crate::TrustTask<super::Payload> =
serde_json::from_str(JSON).expect("deserialize request 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, "request example failed round-trip");
}
#[test]
fn response_example_1() {
const JSON: &str = "{\n \"id\": \"urn:uuid:0b3c9f4e-8a21-4d6b-9e57-3f1a2c4d5e02\",\n \"type\": \"https://trusttasks.org/spec/git-ns/right/break-glass/0.1#response\",\n \"threadId\": \"urn:uuid:0b3c9f4e-8a21-4d6b-9e57-3f1a2c4d5e01\",\n \"issuer\": \"did:webvh:QmVtcScid7:acme-vtc.example\",\n \"recipient\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"issuedAt\": \"2026-09-25T02:10:31Z\",\n \"payload\": {\n \"right\": {\n \"subject\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"right\": \"git.repo.own\",\n \"resource\": \"github.com/acme/widgets\",\n \"grantedBy\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"grantedAt\": \"2026-09-25T02:10:31Z\",\n \"breakGlass\": {\n \"by\": \"did:webvh:QmCarolScid3:acme-vtc.example:carol\",\n \"at\": \"2026-09-25T02:10:31Z\",\n \"justification\": \"CVE-2026-4411 fix must ship tonight; both owners (Alice, Bob) unreachable since 22:00, paged twice. Will ask Dana to ratify in the morning.\"\n }\n }\n },\n \"proof\": {\n \"type\": \"DataIntegrityProof\",\n \"cryptosuite\": \"eddsa-jcs-2022\",\n \"verificationMethod\": \"did:webvh:QmVtcScid7:acme-vtc.example#key-1\",\n \"created\": \"2026-09-25T02:10:31Z\",\n \"proofPurpose\": \"assertionMethod\",\n \"proofValue\": \"z2Kp6Wm9Rb3Tx7Ln1Qc5Hd8Fz2Ya6Js1Vg7Ue9Ti3Ob5Qn2Mk8Cr4Pw6El1Gh9Sy3Ad7Bv5Xf2Lj8Tq4Rz6Kn1M\"\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");
}
/// Each fixture in `payload.invalid-examples.json` MUST be
/// rejected by at least one of: serde deserialization, or
/// JSON-Schema validation under the `validate` feature. The
/// fixture file documents the producer-side bug class that
/// each payload exemplifies; this generated test pins it.
#[cfg(feature = "validate")]
#[test]
fn rejects_invalid_examples() {
use crate::validate::ValidatedPayload;
let fixtures: &[(&str, &str)] = &[
(
"A justification is required: a break-glass nobody explained is the one the audit cannot answer.",
"{\n \"resource\": \"github.com/acme/widgets\",\n \"right\": \"git.repo.own\"\n}",
),
(
"A justification of only whitespace is no justification.",
"{\n \"justification\": \" \\n\\t\",\n \"resource\": \"github.com/acme/widgets\",\n \"right\": \"git.repo.own\"\n}",
),
(
"An empty justification is refused.",
"{\n \"justification\": \"\",\n \"resource\": \"github.com/acme/widgets\",\n \"right\": \"git.repo.own\"\n}",
),
(
"`git.commit.sign` is not elevated: grant it with git-ns/right/grant, which allows it to oneself.",
"{\n \"justification\": \"Hotfix tonight\",\n \"resource\": \"github.com/acme/widgets\",\n \"right\": \"git.commit.sign\"\n}",
),
(
"`git.repo.maintain` is not elevated either.",
"{\n \"justification\": \"Hotfix tonight\",\n \"resource\": \"github.com/acme/widgets\",\n \"right\": \"git.repo.maintain\"\n}",
),
(
"There is no `subject`: a break-glass is always for the actor. Granting someone else goes through git-ns/right/grant.",
"{\n \"justification\": \"Bob is away\",\n \"resource\": \"github.com/acme/widgets\",\n \"right\": \"git.repo.own\",\n \"subject\": \"did:webvh:QmBobScid2:acme-vtc.example:bob\"\n}",
),
(
"There is no `expiresAt`: a break-glass never lapses on its own, because it must still work when nobody else is there to extend it. It ends when it is revoked.",
"{\n \"expiresAt\": \"2026-10-01T00:00:00Z\",\n \"justification\": \"Release blocked; both owners on leave\",\n \"resource\": \"github.com/acme/widgets\",\n \"right\": \"git.repo.own\"\n}",
),
(
"A resource always names its forge.",
"{\n \"justification\": \"Release blocked; both owners on leave\",\n \"resource\": \"acme/widgets\",\n \"right\": \"git.repo.own\"\n}",
),
(
"The justification is bounded.",
"{\n \"justification\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\",\n \"resource\": \"github.com/acme\",\n \"right\": \"git.ns.admin\"\n}",
),
];
for (i, (note, raw)) in fixtures.iter().enumerate() {
let value: serde_json::Value = match serde_json::from_str(raw) {
Ok(v) => v,
Err(_) => continue,
};
let serde_ok = serde_json::from_value::<super::Payload>(value.clone()).is_ok();
let schema_ok = super::Payload::validate_value(&value).is_ok();
assert!(
!(serde_ok && schema_ok),
"invalid-example #{} ({:?}) was accepted by both serde and JSON Schema; \
the fixture's stated failure class is no longer caught:\n{}",
i + 1,
note,
raw
);
}
}
}