//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `vtc/roles/define`. 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())
}
}
}
///Opaque identifier of one administrative power in the maintainer's capability registry — lowercase dot-separated segments (e.g. `keys.sign`, `vtc.roles.assign`, `git.repo.manage`). A consumer MUST NOT treat a capability it does not recognise as granted.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Capability",
/// "description": "Opaque identifier of one administrative power in the maintainer's capability registry — lowercase dot-separated segments (e.g. `keys.sign`, `vtc.roles.assign`, `git.repo.manage`). A consumer MUST NOT treat a capability it does not recognise as granted.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1,
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)*$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Capability(::std::string::String);
impl ::std::ops::Deref for Capability {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Capability> for ::std::string::String {
fn from(value: Capability) -> Self {
value.0
}
}
impl ::std::str::FromStr for Capability {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-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 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()
}
}
impl<'de> ::serde::Deserialize<'de> for Capability {
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 capability, optionally qualified by a resource. Used where a capability is named but not granted — a role's ceiling, a role's approve ceiling, an entry's approvable capabilities. An unqualified reference covers every resource of the capability's kind.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "CapabilityRef",
/// "description": "A capability, optionally qualified by a resource. Used where a capability is named but not granted — a role's ceiling, a role's approve ceiling, an entry's approvable capabilities. An unqualified reference covers every resource of the capability's kind.",
/// "type": "object",
/// "required": [
/// "capability"
/// ],
/// "properties": {
/// "capability": {
/// "$ref": "#/definitions/Capability"
/// },
/// "resource": {
/// "description": "Optional qualifier. Absent covers every resource of the capability's kind.",
/// "$ref": "#/definitions/ResourceQualifier"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct CapabilityRef {
pub capability: Capability,
///Optional qualifier. Absent covers every resource of the capability's kind.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub resource: ::std::option::Option<ResourceQualifier>,
}
impl CapabilityRef {
pub fn builder() -> builder::CapabilityRef {
Default::default()
}
}
///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())
})
}
}
///Create a custom administrative role at a VTC, or replace an existing custom role. Built-in roles are not definable. The outer document members are owned by the framework — SPEC §6.3.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/vtc/roles/define/0.1",
/// "title": "Payload",
/// "description": "Create a custom administrative role at a VTC, or replace an existing custom role. Built-in roles are not definable. The outer document members are owned by the framework — SPEC §6.3.",
/// "type": "object",
/// "required": [
/// "approveScope",
/// "ceiling",
/// "name"
/// ],
/// "properties": {
/// "approveScope": {
/// "description": "The role's approve ceiling, stated in full. Every capability MUST be in the registry and within what the defining administrators may themselves approve. An empty array defines a role that approves nothing.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/CapabilityRef"
/// }
/// },
/// "ceiling": {
/// "description": "The role's capability ceiling, stated in full (it replaces any previous ceiling wholesale). Every capability MUST be in the VTC's registry, MUST NOT be one the registry classes as additive, and MUST be held — at a qualifier at least as wide — by the administrators whose authority defines the role. An empty array defines a role that admits no capability.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/CapabilityRef"
/// }
/// },
/// "description": {
/// "description": "Optional human-readable description of the role.",
/// "type": "string",
/// "maxLength": 512
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "name": {
/// "description": "Name of the custom role. MUST NOT be a built-in role name.",
/// "$ref": "#/definitions/RoleName"
/// },
/// "reason": {
/// "description": "Optional human-readable rationale, recorded with the change and shown to approvers.",
/// "type": "string",
/// "maxLength": 1024
/// },
/// "replaces": {
/// "description": "`true` to replace an existing custom role of this name; `false` or absent to create one. A create against an existing name is refused with `exists`, and a replace against a missing one with `notFound`, so neither intent can be mistaken for the other.",
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///The role's approve ceiling, stated in full. Every capability MUST be in the registry and within what the defining administrators may themselves approve. An empty array defines a role that approves nothing.
#[serde(rename = "approveScope")]
pub approve_scope: ::std::vec::Vec<CapabilityRef>,
///The role's capability ceiling, stated in full (it replaces any previous ceiling wholesale). Every capability MUST be in the VTC's registry, MUST NOT be one the registry classes as additive, and MUST be held — at a qualifier at least as wide — by the administrators whose authority defines the role. An empty array defines a role that admits no capability.
pub ceiling: ::std::vec::Vec<CapabilityRef>,
///Optional human-readable description of the role.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub description: ::std::option::Option<PayloadDescription>,
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Name of the custom role. MUST NOT be a built-in role name.
pub name: RoleName,
///Optional human-readable rationale, recorded with the change and shown to approvers.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub reason: ::std::option::Option<PayloadReason>,
///`true` to replace an existing custom role of this name; `false` or absent to create one. A create against an existing name is refused with `exists`, and a replace against a missing one with `notFound`, so neither intent can be mistaken for the other.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub replaces: ::std::option::Option<bool>,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///Optional human-readable description of the role.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional human-readable description of the role.",
/// "type": "string",
/// "maxLength": 512
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadDescription(::std::string::String);
impl ::std::ops::Deref for PayloadDescription {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadDescription> for ::std::string::String {
fn from(value: PayloadDescription) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadDescription {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 512usize {
return Err("longer than 512 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadDescription {
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 PayloadDescription {
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 PayloadDescription {
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 PayloadDescription {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Optional human-readable rationale, recorded with the change and shown to approvers.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional human-readable rationale, recorded with the change and shown to approvers.",
/// "type": "string",
/// "maxLength": 1024
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadReason(::std::string::String);
impl ::std::ops::Deref for PayloadReason {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadReason> for ::std::string::String {
fn from(value: PayloadReason) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadReason {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 1024usize {
return Err("longer than 1024 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadReason {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadReason {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadReason {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadReason {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Opaque qualifier naming the part of the maintainer's resources a capability applies to (e.g. `git-ns:github.com/acme`, `git-repo:github.com/acme/r#4211`, `policy:join`). Its grammar and containment rule are the maintainer's: a qualified capability covers the named resource and the resources inside it. A qualifier MUST NOT be read as widening the entry's act scope.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "ResourceQualifier",
/// "description": "Opaque qualifier naming the part of the maintainer's resources a capability applies to (e.g. `git-ns:github.com/acme`, `git-repo:github.com/acme/r#4211`, `policy:join`). Its grammar and containment rule are the maintainer's: a qualified capability covers the named resource and the resources inside it. A qualifier MUST NOT be read as widening the entry's act scope.",
/// "type": "string",
/// "maxLength": 512,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResourceQualifier(::std::string::String);
impl ::std::ops::Deref for ResourceQualifier {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResourceQualifier> for ::std::string::String {
fn from(value: ResourceQualifier) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResourceQualifier {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 512usize {
return Err("longer than 512 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 ResourceQualifier {
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 ResourceQualifier {
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 ResourceQualifier {
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 ResourceQualifier {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The success response to a vtc/roles/define/0.1 request. Carried in a Trust Task document whose type is https://trusttasks.org/spec/vtc/roles/define/0.1#response.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "The success response to a vtc/roles/define/0.1 request. Carried in a Trust Task document whose type is https://trusttasks.org/spec/vtc/roles/define/0.1#response.",
/// "type": "object",
/// "required": [
/// "role"
/// ],
/// "properties": {
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "role": {
/// "description": "The role record the VTC now holds, with `builtIn: false` and its provenance.",
/// "$ref": "#/definitions/RoleDefinition"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///The role record the VTC now holds, with `builtIn: false` and its provenance.
pub role: RoleDefinition,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
/**
One administrative role of a VTC: a name, the CEILING of capabilities an entry holding the role may hold, and the approve ceiling of capabilities it may approve.
A ROLE IS A CEILING, NOT A GRANT. It states the most an entry with this role could be allowed; the entry's own `capabilities` say what it does hold, and may be narrower. An entry's effective capabilities are this whole `ceiling` where the entry states `capabilities: {scope: ceiling}`, nothing where it states `none`, and this `ceiling` ∩ its listed grants (plus any additive grant) where it states `listed`. Holding the role confers nothing an entry has not been granted. Likewise `approveScope` bounds what an entry may approve: the entry's `approveCapabilities` select from it (`ceiling` = all of it, `listed` = an intersection, absent or `none` = nothing), and the entry's `approve` scope must not be none for any of it to apply.
NO WILDCARDS. A ceiling lists its capabilities explicitly, even where it is meant to be "everything"; a role whose ceiling is empty holds no capability, and one whose approve ceiling is empty approves none. Built-in roles whose intent is the whole registry enumerate it, and their records grow when the registry grows — by release, never by policy.
BUILT-IN ROLES (`builtIn: true`) are fixed by the VTC implementation and MUST NOT be changed or deleted through vtc/roles/define or vtc/roles/delete. The built-in set, with the ceilings a VTC is expected to ship (descriptive; the authoritative ceiling is the record the VTC returns):
- `community-admin` — ceiling: every `vtc.*` capability and `git.ns.admin` unqualified; approve ceiling: every capability. With its full ceiling it is the community's unrestricted administrator.
- `moderator` — ceiling: `vtc.members.manage`, `vtc.join.decide`, `vtc.invitations.manage`; approve ceiling: the same.
- `vetting-lead` — ceiling: `vtc.vetting.manage` (an entry may qualify it by criterion); approve ceiling: `vtc.vetting.manage`, so vetting leads approve vetter grants and other vetting leads at their qualifier.
- `repo-manager` — ceiling: `git.repo.manage` and `git.ns.admin` (held qualified by namespace or repository); approve ceiling: the same, at the same qualifier.
- `credential-officer` — ceiling: `vtc.credentials.issue`, `vtc.credentials.revoke`; approve ceiling: the same.
- `auditor` — ceiling: `vtc.audit.read`; approve ceiling: none.
- `approver` — ceiling: none (an entry holding it states `act: {scope: none}`); approve ceiling: every capability, narrowed by the entry's `approveCapabilities` — the least-privilege approver.
CUSTOM ROLES (`builtIn: false`) are created, changed and deleted by a community through vtc/roles/define and vtc/roles/delete. A VTC implementing this family takes those changes only through an N-of-M approval among its administrators, never through policy (Rego or otherwise) — authority-defining records are host-enforced, and policy may only refuse. That is a description of the VTC's model, not a requirement this schema can carry. An entry holds at most one administrative role; a combination (repo manager AND vetting lead) is expressed as a custom role.*/
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "RoleDefinition",
/// "description": "One administrative role of a VTC: a name, the CEILING of capabilities an entry holding the role may hold, and the approve ceiling of capabilities it may approve.\n\nA ROLE IS A CEILING, NOT A GRANT. It states the most an entry with this role could be allowed; the entry's own `capabilities` say what it does hold, and may be narrower. An entry's effective capabilities are this whole `ceiling` where the entry states `capabilities: {scope: ceiling}`, nothing where it states `none`, and this `ceiling` ∩ its listed grants (plus any additive grant) where it states `listed`. Holding the role confers nothing an entry has not been granted. Likewise `approveScope` bounds what an entry may approve: the entry's `approveCapabilities` select from it (`ceiling` = all of it, `listed` = an intersection, absent or `none` = nothing), and the entry's `approve` scope must not be none for any of it to apply.\n\nNO WILDCARDS. A ceiling lists its capabilities explicitly, even where it is meant to be \"everything\"; a role whose ceiling is empty holds no capability, and one whose approve ceiling is empty approves none. Built-in roles whose intent is the whole registry enumerate it, and their records grow when the registry grows — by release, never by policy.\n\nBUILT-IN ROLES (`builtIn: true`) are fixed by the VTC implementation and MUST NOT be changed or deleted through vtc/roles/define or vtc/roles/delete. The built-in set, with the ceilings a VTC is expected to ship (descriptive; the authoritative ceiling is the record the VTC returns):\n- `community-admin` — ceiling: every `vtc.*` capability and `git.ns.admin` unqualified; approve ceiling: every capability. With its full ceiling it is the community's unrestricted administrator.\n- `moderator` — ceiling: `vtc.members.manage`, `vtc.join.decide`, `vtc.invitations.manage`; approve ceiling: the same.\n- `vetting-lead` — ceiling: `vtc.vetting.manage` (an entry may qualify it by criterion); approve ceiling: `vtc.vetting.manage`, so vetting leads approve vetter grants and other vetting leads at their qualifier.\n- `repo-manager` — ceiling: `git.repo.manage` and `git.ns.admin` (held qualified by namespace or repository); approve ceiling: the same, at the same qualifier.\n- `credential-officer` — ceiling: `vtc.credentials.issue`, `vtc.credentials.revoke`; approve ceiling: the same.\n- `auditor` — ceiling: `vtc.audit.read`; approve ceiling: none.\n- `approver` — ceiling: none (an entry holding it states `act: {scope: none}`); approve ceiling: every capability, narrowed by the entry's `approveCapabilities` — the least-privilege approver.\n\nCUSTOM ROLES (`builtIn: false`) are created, changed and deleted by a community through vtc/roles/define and vtc/roles/delete. A VTC implementing this family takes those changes only through an N-of-M approval among its administrators, never through policy (Rego or otherwise) — authority-defining records are host-enforced, and policy may only refuse. That is a description of the VTC's model, not a requirement this schema can carry. An entry holds at most one administrative role; a combination (repo manager AND vetting lead) is expressed as a custom role.",
/// "type": "object",
/// "required": [
/// "approveScope",
/// "builtIn",
/// "ceiling",
/// "name"
/// ],
/// "properties": {
/// "approveScope": {
/// "description": "The capabilities an entry holding this role MAY approve an action needing — the role's approve ceiling, independent of `ceiling`. An entry approves all of it with `approveCapabilities: {scope: ceiling}`, an intersection with `listed`, and nothing when `approveCapabilities` is absent or `none`; the entry's `approve` scope must be other than none for any of it to apply. EMPTY means the role approves nothing.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/CapabilityRef"
/// }
/// },
/// "builtIn": {
/// "description": "`true` for a role fixed by the VTC implementation, which cannot be changed or deleted; `false` for a community-defined custom role.",
/// "type": "boolean"
/// },
/// "ceiling": {
/// "description": "The capabilities an entry holding this role MAY hold, each optionally qualified by a resource. An entry may hold a capability at a qualifier inside the ceiling's (a ceiling of `git.repo.manage` unqualified admits `git.repo.manage @ git-ns:github.com/acme`), never outside it. EMPTY means the role admits no capability. A capability the VTC classes as additive never appears in a ceiling — an additive capability is by definition one no role implies.",
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/CapabilityRef"
/// }
/// },
/// "createdAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "createdBy": {
/// "description": "VID of the administrator whose request created the role (for a custom role created by approval, the requester). Absent on a built-in role.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1
/// },
/// "description": {
/// "description": "Optional human-readable description. Confers nothing.",
/// "type": "string",
/// "maxLength": 512
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1. An extension member MUST NOT be interpreted as widening a ceiling.",
/// "$ref": "#/definitions/Ext"
/// },
/// "name": {
/// "$ref": "#/definitions/RoleName"
/// },
/// "updatedAt": {
/// "type": "string",
/// "format": "date-time"
/// },
/// "updatedBy": {
/// "description": "VID of the administrator whose request last replaced the role. Absent on a built-in role and on a custom role never replaced.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct RoleDefinition {
///The capabilities an entry holding this role MAY approve an action needing — the role's approve ceiling, independent of `ceiling`. An entry approves all of it with `approveCapabilities: {scope: ceiling}`, an intersection with `listed`, and nothing when `approveCapabilities` is absent or `none`; the entry's `approve` scope must be other than none for any of it to apply. EMPTY means the role approves nothing.
#[serde(rename = "approveScope")]
pub approve_scope: ::std::vec::Vec<CapabilityRef>,
///`true` for a role fixed by the VTC implementation, which cannot be changed or deleted; `false` for a community-defined custom role.
#[serde(rename = "builtIn")]
pub built_in: bool,
///The capabilities an entry holding this role MAY hold, each optionally qualified by a resource. An entry may hold a capability at a qualifier inside the ceiling's (a ceiling of `git.repo.manage` unqualified admits `git.repo.manage @ git-ns:github.com/acme`), never outside it. EMPTY means the role admits no capability. A capability the VTC classes as additive never appears in a ceiling — an additive capability is by definition one no role implies.
pub ceiling: ::std::vec::Vec<CapabilityRef>,
#[serde(
rename = "createdAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub created_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///VID of the administrator whose request created the role (for a custom role created by approval, the requester). Absent on a built-in role.
#[serde(
rename = "createdBy",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub created_by: ::std::option::Option<RoleDefinitionCreatedBy>,
///Optional human-readable description. Confers nothing.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub description: ::std::option::Option<RoleDefinitionDescription>,
///Ecosystem-defined extension members per SPEC.md §4.5.1. An extension member MUST NOT be interpreted as widening a ceiling.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
pub name: RoleName,
#[serde(
rename = "updatedAt",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub updated_at: ::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
///VID of the administrator whose request last replaced the role. Absent on a built-in role and on a custom role never replaced.
#[serde(
rename = "updatedBy",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub updated_by: ::std::option::Option<RoleDefinitionUpdatedBy>,
}
impl RoleDefinition {
pub fn builder() -> builder::RoleDefinition {
Default::default()
}
}
///VID of the administrator whose request created the role (for a custom role created by approval, the requester). Absent on a built-in role.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "VID of the administrator whose request created the role (for a custom role created by approval, the requester). Absent on a built-in role.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct RoleDefinitionCreatedBy(::std::string::String);
impl ::std::ops::Deref for RoleDefinitionCreatedBy {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<RoleDefinitionCreatedBy> for ::std::string::String {
fn from(value: RoleDefinitionCreatedBy) -> Self {
value.0
}
}
impl ::std::str::FromStr for RoleDefinitionCreatedBy {
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());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for RoleDefinitionCreatedBy {
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 RoleDefinitionCreatedBy {
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 RoleDefinitionCreatedBy {
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 RoleDefinitionCreatedBy {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Optional human-readable description. Confers nothing.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional human-readable description. Confers nothing.",
/// "type": "string",
/// "maxLength": 512
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct RoleDefinitionDescription(::std::string::String);
impl ::std::ops::Deref for RoleDefinitionDescription {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<RoleDefinitionDescription> for ::std::string::String {
fn from(value: RoleDefinitionDescription) -> Self {
value.0
}
}
impl ::std::str::FromStr for RoleDefinitionDescription {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 512usize {
return Err("longer than 512 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for RoleDefinitionDescription {
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 RoleDefinitionDescription {
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 RoleDefinitionDescription {
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 RoleDefinitionDescription {
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())
})
}
}
///VID of the administrator whose request last replaced the role. Absent on a built-in role and on a custom role never replaced.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "VID of the administrator whose request last replaced the role. Absent on a built-in role and on a custom role never replaced.",
/// "type": "string",
/// "maxLength": 2048,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct RoleDefinitionUpdatedBy(::std::string::String);
impl ::std::ops::Deref for RoleDefinitionUpdatedBy {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<RoleDefinitionUpdatedBy> for ::std::string::String {
fn from(value: RoleDefinitionUpdatedBy) -> Self {
value.0
}
}
impl ::std::str::FromStr for RoleDefinitionUpdatedBy {
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());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for RoleDefinitionUpdatedBy {
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 RoleDefinitionUpdatedBy {
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 RoleDefinitionUpdatedBy {
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 RoleDefinitionUpdatedBy {
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 role identifier: lowercase, hyphen-separated, at most 64 characters. Compared by exact string equality. It is the value an AclEntry 0.2 carries in `role`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "RoleName",
/// "description": "A role identifier: lowercase, hyphen-separated, at most 64 characters. Compared by exact string equality. It is the value an AclEntry 0.2 carries in `role`.",
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]{0,63}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct RoleName(::std::string::String);
impl ::std::ops::Deref for RoleName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<RoleName> for ::std::string::String {
fn from(value: RoleName) -> Self {
value.0
}
}
impl ::std::str::FromStr for RoleName {
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-]{0,63}$").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-z0-9-]{0,63}$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for RoleName {
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 RoleName {
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 RoleName {
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 RoleName {
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 CapabilityRef {
capability: ::std::result::Result<super::Capability, ::std::string::String>,
resource: ::std::result::Result<
::std::option::Option<super::ResourceQualifier>,
::std::string::String,
>,
}
impl ::std::default::Default for CapabilityRef {
fn default() -> Self {
Self {
capability: Err("no value supplied for capability".to_string()),
resource: Ok(Default::default()),
}
}
}
impl CapabilityRef {
pub fn capability<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::Capability>,
T::Error: ::std::fmt::Display,
{
self.capability = value
.try_into()
.map_err(|e| format!("error converting supplied value for capability: {e}"));
self
}
pub fn resource<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::ResourceQualifier>>,
T::Error: ::std::fmt::Display,
{
self.resource = value
.try_into()
.map_err(|e| format!("error converting supplied value for resource: {e}"));
self
}
}
impl ::std::convert::TryFrom<CapabilityRef> for super::CapabilityRef {
type Error = super::error::ConversionError;
fn try_from(
value: CapabilityRef,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
capability: value.capability?,
resource: value.resource?,
})
}
}
impl ::std::convert::From<super::CapabilityRef> for CapabilityRef {
fn from(value: super::CapabilityRef) -> Self {
Self {
capability: Ok(value.capability),
resource: Ok(value.resource),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
approve_scope:
::std::result::Result<::std::vec::Vec<super::CapabilityRef>, ::std::string::String>,
ceiling:
::std::result::Result<::std::vec::Vec<super::CapabilityRef>, ::std::string::String>,
description: ::std::result::Result<
::std::option::Option<super::PayloadDescription>,
::std::string::String,
>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
name: ::std::result::Result<super::RoleName, ::std::string::String>,
reason: ::std::result::Result<
::std::option::Option<super::PayloadReason>,
::std::string::String,
>,
replaces: ::std::result::Result<::std::option::Option<bool>, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
approve_scope: Err("no value supplied for approve_scope".to_string()),
ceiling: Err("no value supplied for ceiling".to_string()),
description: Ok(Default::default()),
ext: Ok(Default::default()),
name: Err("no value supplied for name".to_string()),
reason: Ok(Default::default()),
replaces: Ok(Default::default()),
}
}
}
impl Payload {
pub fn approve_scope<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::CapabilityRef>>,
T::Error: ::std::fmt::Display,
{
self.approve_scope = value
.try_into()
.map_err(|e| format!("error converting supplied value for approve_scope: {e}"));
self
}
pub fn ceiling<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::CapabilityRef>>,
T::Error: ::std::fmt::Display,
{
self.ceiling = value
.try_into()
.map_err(|e| format!("error converting supplied value for ceiling: {e}"));
self
}
pub fn description<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PayloadDescription>>,
T::Error: ::std::fmt::Display,
{
self.description = value
.try_into()
.map_err(|e| format!("error converting supplied value for description: {e}"));
self
}
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn name<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::RoleName>,
T::Error: ::std::fmt::Display,
{
self.name = value
.try_into()
.map_err(|e| format!("error converting supplied value for name: {e}"));
self
}
pub fn reason<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::PayloadReason>>,
T::Error: ::std::fmt::Display,
{
self.reason = value
.try_into()
.map_err(|e| format!("error converting supplied value for reason: {e}"));
self
}
pub fn replaces<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<bool>>,
T::Error: ::std::fmt::Display,
{
self.replaces = value
.try_into()
.map_err(|e| format!("error converting supplied value for replaces: {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 {
approve_scope: value.approve_scope?,
ceiling: value.ceiling?,
description: value.description?,
ext: value.ext?,
name: value.name?,
reason: value.reason?,
replaces: value.replaces?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
approve_scope: Ok(value.approve_scope),
ceiling: Ok(value.ceiling),
description: Ok(value.description),
ext: Ok(value.ext),
name: Ok(value.name),
reason: Ok(value.reason),
replaces: Ok(value.replaces),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
role: ::std::result::Result<super::RoleDefinition, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
ext: Ok(Default::default()),
role: Err("no value supplied for role".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 role<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::RoleDefinition>,
T::Error: ::std::fmt::Display,
{
self.role = value
.try_into()
.map_err(|e| format!("error converting supplied value for role: {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?,
role: value.role?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
ext: Ok(value.ext),
role: Ok(value.role),
}
}
}
#[derive(Clone, Debug)]
pub struct RoleDefinition {
approve_scope:
::std::result::Result<::std::vec::Vec<super::CapabilityRef>, ::std::string::String>,
built_in: ::std::result::Result<bool, ::std::string::String>,
ceiling:
::std::result::Result<::std::vec::Vec<super::CapabilityRef>, ::std::string::String>,
created_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
created_by: ::std::result::Result<
::std::option::Option<super::RoleDefinitionCreatedBy>,
::std::string::String,
>,
description: ::std::result::Result<
::std::option::Option<super::RoleDefinitionDescription>,
::std::string::String,
>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
name: ::std::result::Result<super::RoleName, ::std::string::String>,
updated_at: ::std::result::Result<
::std::option::Option<::chrono::DateTime<::chrono::offset::Utc>>,
::std::string::String,
>,
updated_by: ::std::result::Result<
::std::option::Option<super::RoleDefinitionUpdatedBy>,
::std::string::String,
>,
}
impl ::std::default::Default for RoleDefinition {
fn default() -> Self {
Self {
approve_scope: Err("no value supplied for approve_scope".to_string()),
built_in: Err("no value supplied for built_in".to_string()),
ceiling: Err("no value supplied for ceiling".to_string()),
created_at: Ok(Default::default()),
created_by: Ok(Default::default()),
description: Ok(Default::default()),
ext: Ok(Default::default()),
name: Err("no value supplied for name".to_string()),
updated_at: Ok(Default::default()),
updated_by: Ok(Default::default()),
}
}
}
impl RoleDefinition {
pub fn approve_scope<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::CapabilityRef>>,
T::Error: ::std::fmt::Display,
{
self.approve_scope = value
.try_into()
.map_err(|e| format!("error converting supplied value for approve_scope: {e}"));
self
}
pub fn built_in<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<bool>,
T::Error: ::std::fmt::Display,
{
self.built_in = value
.try_into()
.map_err(|e| format!("error converting supplied value for built_in: {e}"));
self
}
pub fn ceiling<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::CapabilityRef>>,
T::Error: ::std::fmt::Display,
{
self.ceiling = value
.try_into()
.map_err(|e| format!("error converting supplied value for ceiling: {e}"));
self
}
pub fn created_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.created_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for created_at: {e}"));
self
}
pub fn created_by<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RoleDefinitionCreatedBy>>,
T::Error: ::std::fmt::Display,
{
self.created_by = value
.try_into()
.map_err(|e| format!("error converting supplied value for created_by: {e}"));
self
}
pub fn description<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RoleDefinitionDescription>>,
T::Error: ::std::fmt::Display,
{
self.description = value
.try_into()
.map_err(|e| format!("error converting supplied value for description: {e}"));
self
}
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn name<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::RoleName>,
T::Error: ::std::fmt::Display,
{
self.name = value
.try_into()
.map_err(|e| format!("error converting supplied value for name: {e}"));
self
}
pub fn updated_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.updated_at = value
.try_into()
.map_err(|e| format!("error converting supplied value for updated_at: {e}"));
self
}
pub fn updated_by<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::RoleDefinitionUpdatedBy>>,
T::Error: ::std::fmt::Display,
{
self.updated_by = value
.try_into()
.map_err(|e| format!("error converting supplied value for updated_by: {e}"));
self
}
}
impl ::std::convert::TryFrom<RoleDefinition> for super::RoleDefinition {
type Error = super::error::ConversionError;
fn try_from(
value: RoleDefinition,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
approve_scope: value.approve_scope?,
built_in: value.built_in?,
ceiling: value.ceiling?,
created_at: value.created_at?,
created_by: value.created_by?,
description: value.description?,
ext: value.ext?,
name: value.name?,
updated_at: value.updated_at?,
updated_by: value.updated_by?,
})
}
}
impl ::std::convert::From<super::RoleDefinition> for RoleDefinition {
fn from(value: super::RoleDefinition) -> Self {
Self {
approve_scope: Ok(value.approve_scope),
built_in: Ok(value.built_in),
ceiling: Ok(value.ceiling),
created_at: Ok(value.created_at),
created_by: Ok(value.created_by),
description: Ok(value.description),
ext: Ok(value.ext),
name: Ok(value.name),
updated_at: Ok(value.updated_at),
updated_by: Ok(value.updated_by),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/roles/define/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 \"Capability\": {\n \"description\": \"Opaque identifier of one administrative power in the maintainer's capability registry — lowercase dot-separated segments (e.g. `keys.sign`, `vtc.roles.assign`, `git.repo.manage`). A consumer MUST NOT treat a capability it does not recognise as granted.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)*$\",\n \"title\": \"Capability\",\n \"type\": \"string\"\n },\n \"CapabilityRef\": {\n \"additionalProperties\": false,\n \"description\": \"A capability, optionally qualified by a resource. Used where a capability is named but not granted — a role's ceiling, a role's approve ceiling, an entry's approvable capabilities. An unqualified reference covers every resource of the capability's kind.\",\n \"properties\": {\n \"capability\": {\n \"$ref\": \"#/$defs/Capability\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/ResourceQualifier\",\n \"description\": \"Optional qualifier. Absent covers every resource of the capability's kind.\"\n }\n },\n \"required\": [\n \"capability\"\n ],\n \"title\": \"CapabilityRef\",\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 \"ResourceQualifier\": {\n \"description\": \"Opaque qualifier naming the part of the maintainer's resources a capability applies to (e.g. `git-ns:github.com/acme`, `git-repo:github.com/acme/r#4211`, `policy:join`). Its grammar and containment rule are the maintainer's: a qualified capability covers the named resource and the resources inside it. A qualifier MUST NOT be read as widening the entry's act scope.\",\n \"maxLength\": 512,\n \"minLength\": 1,\n \"title\": \"ResourceQualifier\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"The success response to a vtc/roles/define/0.1 request. Carried in a Trust Task document whose type is https://trusttasks.org/spec/vtc/roles/define/0.1#response.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"role\": {\n \"$ref\": \"#/$defs/RoleDefinition\",\n \"description\": \"The role record the VTC now holds, with `builtIn: false` and its provenance.\"\n }\n },\n \"required\": [\n \"role\"\n ],\n \"title\": \"VTC Roles — Define — response payload\",\n \"type\": \"object\"\n },\n \"RoleDefinition\": {\n \"additionalProperties\": false,\n \"description\": \"One administrative role of a VTC: a name, the CEILING of capabilities an entry holding the role may hold, and the approve ceiling of capabilities it may approve.\\n\\nA ROLE IS A CEILING, NOT A GRANT. It states the most an entry with this role could be allowed; the entry's own `capabilities` say what it does hold, and may be narrower. An entry's effective capabilities are this whole `ceiling` where the entry states `capabilities: {scope: ceiling}`, nothing where it states `none`, and this `ceiling` ∩ its listed grants (plus any additive grant) where it states `listed`. Holding the role confers nothing an entry has not been granted. Likewise `approveScope` bounds what an entry may approve: the entry's `approveCapabilities` select from it (`ceiling` = all of it, `listed` = an intersection, absent or `none` = nothing), and the entry's `approve` scope must not be none for any of it to apply.\\n\\nNO WILDCARDS. A ceiling lists its capabilities explicitly, even where it is meant to be \\\"everything\\\"; a role whose ceiling is empty holds no capability, and one whose approve ceiling is empty approves none. Built-in roles whose intent is the whole registry enumerate it, and their records grow when the registry grows — by release, never by policy.\\n\\nBUILT-IN ROLES (`builtIn: true`) are fixed by the VTC implementation and MUST NOT be changed or deleted through vtc/roles/define or vtc/roles/delete. The built-in set, with the ceilings a VTC is expected to ship (descriptive; the authoritative ceiling is the record the VTC returns):\\n- `community-admin` — ceiling: every `vtc.*` capability and `git.ns.admin` unqualified; approve ceiling: every capability. With its full ceiling it is the community's unrestricted administrator.\\n- `moderator` — ceiling: `vtc.members.manage`, `vtc.join.decide`, `vtc.invitations.manage`; approve ceiling: the same.\\n- `vetting-lead` — ceiling: `vtc.vetting.manage` (an entry may qualify it by criterion); approve ceiling: `vtc.vetting.manage`, so vetting leads approve vetter grants and other vetting leads at their qualifier.\\n- `repo-manager` — ceiling: `git.repo.manage` and `git.ns.admin` (held qualified by namespace or repository); approve ceiling: the same, at the same qualifier.\\n- `credential-officer` — ceiling: `vtc.credentials.issue`, `vtc.credentials.revoke`; approve ceiling: the same.\\n- `auditor` — ceiling: `vtc.audit.read`; approve ceiling: none.\\n- `approver` — ceiling: none (an entry holding it states `act: {scope: none}`); approve ceiling: every capability, narrowed by the entry's `approveCapabilities` — the least-privilege approver.\\n\\nCUSTOM ROLES (`builtIn: false`) are created, changed and deleted by a community through vtc/roles/define and vtc/roles/delete. A VTC implementing this family takes those changes only through an N-of-M approval among its administrators, never through policy (Rego or otherwise) — authority-defining records are host-enforced, and policy may only refuse. That is a description of the VTC's model, not a requirement this schema can carry. An entry holds at most one administrative role; a combination (repo manager AND vetting lead) is expressed as a custom role.\",\n \"properties\": {\n \"approveScope\": {\n \"description\": \"The capabilities an entry holding this role MAY approve an action needing — the role's approve ceiling, independent of `ceiling`. An entry approves all of it with `approveCapabilities: {scope: ceiling}`, an intersection with `listed`, and nothing when `approveCapabilities` is absent or `none`; the entry's `approve` scope must be other than none for any of it to apply. EMPTY means the role approves nothing.\",\n \"items\": {\n \"$ref\": \"#/$defs/CapabilityRef\"\n },\n \"type\": \"array\"\n },\n \"builtIn\": {\n \"description\": \"`true` for a role fixed by the VTC implementation, which cannot be changed or deleted; `false` for a community-defined custom role.\",\n \"type\": \"boolean\"\n },\n \"ceiling\": {\n \"description\": \"The capabilities an entry holding this role MAY hold, each optionally qualified by a resource. An entry may hold a capability at a qualifier inside the ceiling's (a ceiling of `git.repo.manage` unqualified admits `git.repo.manage @ git-ns:github.com/acme`), never outside it. EMPTY means the role admits no capability. A capability the VTC classes as additive never appears in a ceiling — an additive capability is by definition one no role implies.\",\n \"items\": {\n \"$ref\": \"#/$defs/CapabilityRef\"\n },\n \"type\": \"array\"\n },\n \"createdAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"createdBy\": {\n \"description\": \"VID of the administrator whose request created the role (for a custom role created by approval, the requester). Absent on a built-in role.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"description\": {\n \"description\": \"Optional human-readable description. Confers nothing.\",\n \"maxLength\": 512,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1. An extension member MUST NOT be interpreted as widening a ceiling.\"\n },\n \"name\": {\n \"$ref\": \"#/$defs/RoleName\"\n },\n \"updatedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"updatedBy\": {\n \"description\": \"VID of the administrator whose request last replaced the role. Absent on a built-in role and on a custom role never replaced.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"name\",\n \"builtIn\",\n \"ceiling\",\n \"approveScope\"\n ],\n \"title\": \"RoleDefinition\",\n \"type\": \"object\"\n },\n \"RoleName\": {\n \"description\": \"A role identifier: lowercase, hyphen-separated, at most 64 characters. Compared by exact string equality. It is the value an AclEntry 0.2 carries in `role`.\",\n \"pattern\": \"^[a-z][a-z0-9-]{0,63}$\",\n \"title\": \"RoleName\",\n \"type\": \"string\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/vtc/roles/define/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"Create a custom administrative role at a VTC, or replace an existing custom role. Built-in roles are not definable. The outer document members are owned by the framework — SPEC §6.3.\",\n \"properties\": {\n \"approveScope\": {\n \"description\": \"The role's approve ceiling, stated in full. Every capability MUST be in the registry and within what the defining administrators may themselves approve. An empty array defines a role that approves nothing.\",\n \"items\": {\n \"$ref\": \"#/$defs/CapabilityRef\"\n },\n \"type\": \"array\"\n },\n \"ceiling\": {\n \"description\": \"The role's capability ceiling, stated in full (it replaces any previous ceiling wholesale). Every capability MUST be in the VTC's registry, MUST NOT be one the registry classes as additive, and MUST be held — at a qualifier at least as wide — by the administrators whose authority defines the role. An empty array defines a role that admits no capability.\",\n \"items\": {\n \"$ref\": \"#/$defs/CapabilityRef\"\n },\n \"type\": \"array\"\n },\n \"description\": {\n \"description\": \"Optional human-readable description of the role.\",\n \"maxLength\": 512,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"name\": {\n \"$ref\": \"#/$defs/RoleName\",\n \"description\": \"Name of the custom role. MUST NOT be a built-in role name.\"\n },\n \"reason\": {\n \"description\": \"Optional human-readable rationale, recorded with the change and shown to approvers.\",\n \"maxLength\": 1024,\n \"type\": \"string\"\n },\n \"replaces\": {\n \"description\": \"`true` to replace an existing custom role of this name; `false` or absent to create one. A create against an existing name is refused with `exists`, and a replace against a missing one with `notFound`, so neither intent can be mistaken for the other.\",\n \"type\": \"boolean\"\n }\n },\n \"required\": [\n \"name\",\n \"ceiling\",\n \"approveScope\"\n ],\n \"title\": \"VTC Roles — Define — payload\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/vtc/roles/define/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 \"Capability\": {\n \"description\": \"Opaque identifier of one administrative power in the maintainer's capability registry — lowercase dot-separated segments (e.g. `keys.sign`, `vtc.roles.assign`, `git.repo.manage`). A consumer MUST NOT treat a capability it does not recognise as granted.\",\n \"maxLength\": 128,\n \"minLength\": 1,\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)*$\",\n \"title\": \"Capability\",\n \"type\": \"string\"\n },\n \"CapabilityRef\": {\n \"additionalProperties\": false,\n \"description\": \"A capability, optionally qualified by a resource. Used where a capability is named but not granted — a role's ceiling, a role's approve ceiling, an entry's approvable capabilities. An unqualified reference covers every resource of the capability's kind.\",\n \"properties\": {\n \"capability\": {\n \"$ref\": \"#/$defs/Capability\"\n },\n \"resource\": {\n \"$ref\": \"#/$defs/ResourceQualifier\",\n \"description\": \"Optional qualifier. Absent covers every resource of the capability's kind.\"\n }\n },\n \"required\": [\n \"capability\"\n ],\n \"title\": \"CapabilityRef\",\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 \"ResourceQualifier\": {\n \"description\": \"Opaque qualifier naming the part of the maintainer's resources a capability applies to (e.g. `git-ns:github.com/acme`, `git-repo:github.com/acme/r#4211`, `policy:join`). Its grammar and containment rule are the maintainer's: a qualified capability covers the named resource and the resources inside it. A qualifier MUST NOT be read as widening the entry's act scope.\",\n \"maxLength\": 512,\n \"minLength\": 1,\n \"title\": \"ResourceQualifier\",\n \"type\": \"string\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"The success response to a vtc/roles/define/0.1 request. Carried in a Trust Task document whose type is https://trusttasks.org/spec/vtc/roles/define/0.1#response.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"role\": {\n \"$ref\": \"#/$defs/RoleDefinition\",\n \"description\": \"The role record the VTC now holds, with `builtIn: false` and its provenance.\"\n }\n },\n \"required\": [\n \"role\"\n ],\n \"title\": \"VTC Roles — Define — response payload\",\n \"type\": \"object\"\n },\n \"RoleDefinition\": {\n \"additionalProperties\": false,\n \"description\": \"One administrative role of a VTC: a name, the CEILING of capabilities an entry holding the role may hold, and the approve ceiling of capabilities it may approve.\\n\\nA ROLE IS A CEILING, NOT A GRANT. It states the most an entry with this role could be allowed; the entry's own `capabilities` say what it does hold, and may be narrower. An entry's effective capabilities are this whole `ceiling` where the entry states `capabilities: {scope: ceiling}`, nothing where it states `none`, and this `ceiling` ∩ its listed grants (plus any additive grant) where it states `listed`. Holding the role confers nothing an entry has not been granted. Likewise `approveScope` bounds what an entry may approve: the entry's `approveCapabilities` select from it (`ceiling` = all of it, `listed` = an intersection, absent or `none` = nothing), and the entry's `approve` scope must not be none for any of it to apply.\\n\\nNO WILDCARDS. A ceiling lists its capabilities explicitly, even where it is meant to be \\\"everything\\\"; a role whose ceiling is empty holds no capability, and one whose approve ceiling is empty approves none. Built-in roles whose intent is the whole registry enumerate it, and their records grow when the registry grows — by release, never by policy.\\n\\nBUILT-IN ROLES (`builtIn: true`) are fixed by the VTC implementation and MUST NOT be changed or deleted through vtc/roles/define or vtc/roles/delete. The built-in set, with the ceilings a VTC is expected to ship (descriptive; the authoritative ceiling is the record the VTC returns):\\n- `community-admin` — ceiling: every `vtc.*` capability and `git.ns.admin` unqualified; approve ceiling: every capability. With its full ceiling it is the community's unrestricted administrator.\\n- `moderator` — ceiling: `vtc.members.manage`, `vtc.join.decide`, `vtc.invitations.manage`; approve ceiling: the same.\\n- `vetting-lead` — ceiling: `vtc.vetting.manage` (an entry may qualify it by criterion); approve ceiling: `vtc.vetting.manage`, so vetting leads approve vetter grants and other vetting leads at their qualifier.\\n- `repo-manager` — ceiling: `git.repo.manage` and `git.ns.admin` (held qualified by namespace or repository); approve ceiling: the same, at the same qualifier.\\n- `credential-officer` — ceiling: `vtc.credentials.issue`, `vtc.credentials.revoke`; approve ceiling: the same.\\n- `auditor` — ceiling: `vtc.audit.read`; approve ceiling: none.\\n- `approver` — ceiling: none (an entry holding it states `act: {scope: none}`); approve ceiling: every capability, narrowed by the entry's `approveCapabilities` — the least-privilege approver.\\n\\nCUSTOM ROLES (`builtIn: false`) are created, changed and deleted by a community through vtc/roles/define and vtc/roles/delete. A VTC implementing this family takes those changes only through an N-of-M approval among its administrators, never through policy (Rego or otherwise) — authority-defining records are host-enforced, and policy may only refuse. That is a description of the VTC's model, not a requirement this schema can carry. An entry holds at most one administrative role; a combination (repo manager AND vetting lead) is expressed as a custom role.\",\n \"properties\": {\n \"approveScope\": {\n \"description\": \"The capabilities an entry holding this role MAY approve an action needing — the role's approve ceiling, independent of `ceiling`. An entry approves all of it with `approveCapabilities: {scope: ceiling}`, an intersection with `listed`, and nothing when `approveCapabilities` is absent or `none`; the entry's `approve` scope must be other than none for any of it to apply. EMPTY means the role approves nothing.\",\n \"items\": {\n \"$ref\": \"#/$defs/CapabilityRef\"\n },\n \"type\": \"array\"\n },\n \"builtIn\": {\n \"description\": \"`true` for a role fixed by the VTC implementation, which cannot be changed or deleted; `false` for a community-defined custom role.\",\n \"type\": \"boolean\"\n },\n \"ceiling\": {\n \"description\": \"The capabilities an entry holding this role MAY hold, each optionally qualified by a resource. An entry may hold a capability at a qualifier inside the ceiling's (a ceiling of `git.repo.manage` unqualified admits `git.repo.manage @ git-ns:github.com/acme`), never outside it. EMPTY means the role admits no capability. A capability the VTC classes as additive never appears in a ceiling — an additive capability is by definition one no role implies.\",\n \"items\": {\n \"$ref\": \"#/$defs/CapabilityRef\"\n },\n \"type\": \"array\"\n },\n \"createdAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"createdBy\": {\n \"description\": \"VID of the administrator whose request created the role (for a custom role created by approval, the requester). Absent on a built-in role.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"description\": {\n \"description\": \"Optional human-readable description. Confers nothing.\",\n \"maxLength\": 512,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1. An extension member MUST NOT be interpreted as widening a ceiling.\"\n },\n \"name\": {\n \"$ref\": \"#/$defs/RoleName\"\n },\n \"updatedAt\": {\n \"format\": \"date-time\",\n \"type\": \"string\"\n },\n \"updatedBy\": {\n \"description\": \"VID of the administrator whose request last replaced the role. Absent on a built-in role and on a custom role never replaced.\",\n \"maxLength\": 2048,\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"name\",\n \"builtIn\",\n \"ceiling\",\n \"approveScope\"\n ],\n \"title\": \"RoleDefinition\",\n \"type\": \"object\"\n },\n \"RoleName\": {\n \"description\": \"A role identifier: lowercase, hyphen-separated, at most 64 characters. Compared by exact string equality. It is the value an AclEntry 0.2 carries in `role`.\",\n \"pattern\": \"^[a-z][a-z0-9-]{0,63}$\",\n \"title\": \"RoleName\",\n \"type\": \"string\"\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::BUILT_IN_ROLE,
error_codes::EXISTS,
error_codes::NOT_FOUND,
error_codes::UNKNOWN_CAPABILITY,
error_codes::ADDITIVE_CAPABILITY,
error_codes::EXCEEDS_DEFINER_AUTHORITY,
];
/// One constant per extended error code this specification declares
/// (SPEC §7.3 item 9), named for its local part.
///
/// Emit these rather than a string literal: the code is read from the
/// specification, so it cannot name a code the specification never
/// declared.
pub mod error_codes {
/// `vtc/roles/define:builtInRole`
///
/// The name is a built-in role, which cannot be created, changed or replaced.
///
/// Declared `retryable: false`.
pub const BUILT_IN_ROLE: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/roles/define:builtInRole",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/roles/define:exists`
///
/// A custom role of this name already exists and `replaces` is not true.
///
/// Declared `retryable: false`.
pub const EXISTS: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/roles/define:exists",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/roles/define:notFound`
///
/// `replaces` is true and no custom role of this name exists.
///
/// Declared `retryable: false`.
pub const NOT_FOUND: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/roles/define:notFound",
retryable: false,
details_schema: ::core::option::Option::None,
};
/// `vtc/roles/define:unknownCapability`
///
/// A capability in `ceiling` or `approveScope` is not in the VTC's capability registry.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const UNKNOWN_CAPABILITY: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/roles/define:unknownCapability",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"capabilities\":{\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"capabilities\"],\"type\":\"object\"}",
),
};
/// `vtc/roles/define:additiveCapability`
///
/// The ceiling names a capability the registry classes as additive. An additive capability is one no role implies; naming it in a ceiling would let a granter without unrestricted authority confer it.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const ADDITIVE_CAPABILITY: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/roles/define:additiveCapability",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"capabilities\":{\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"capabilities\"],\"type\":\"object\"}",
),
};
/// `vtc/roles/define:exceedsDefinerAuthority`
///
/// The ceiling or approve ceiling names a capability (or a qualifier wider than one) that the administrators whose authority defines the role do not themselves hold, or may not themselves approve.
///
/// Declared `retryable: false`.
/// Declares a `detailsSchema` for the error's `details` member.
pub const EXCEEDS_DEFINER_AUTHORITY: crate::DeclaredErrorCode = crate::DeclaredErrorCode {
code: "vtc/roles/define:exceedsDefinerAuthority",
retryable: false,
details_schema: ::core::option::Option::Some(
"{\"additionalProperties\":false,\"properties\":{\"capabilities\":{\"items\":{\"type\":\"string\"},\"type\":\"array\"}},\"required\":[\"capabilities\"],\"type\":\"object\"}",
),
};
}