pub struct SecretConfig {
pub schema_version: u32,
pub name: String,
pub vault_slot: String,
pub description: Option<String>,
pub encoding: SecretEncoding,
pub access: SecretAccess,
pub target: Option<SecretTargetDecl>,
}Expand description
A camp’s declaration of one cluster secret, from
.yah/infra/secrets/<slug>.toml.
This is the authoring side of the fleet’s cluster-secret store: it names
where the value lives in the camp (a fob vault slot), what the fleet should
call it, and — the point of R706 — which workloads are allowed to mount it.
The declaration is not itself the enforcement point. yah cloud secret put
reads this file, seals the vault value under the cluster KEK, and ships the
ciphertext with its access rule into raft; yubaba’s ClusterResolver
evaluates the rule on the node at mount time. Deleting this file does not
revoke anything — the record in raft is the live authority. That asymmetry is
deliberate: a rule that lived only in a git-tracked camp file would be
trivially bypassed by anyone who could reach the fleet without the camp.
#:schema ../../schema/secret.toml.schema.json
schema_version = 1
name = "cheers/cloud-admin/verify-key"
vault_slot = "cheers-cloud-admin-verify-key"
description = "Ed25519 public key yah-cloud-admin verifies operator PASETOs with"
[access]
workloads = [{ workload = "yah-cloud-admin" }]
[target]
kind = "file"
path = "/run/secrets/cheers-verify.key"
mode = 0o400Fields§
§schema_version: u32§name: StringLogical cluster-secret key, as SecretRef::Cluster { name } spells it —
e.g. "tls/yah.dev/cert", "cheers/cloud-admin/verify-key". May contain
/; the file stem is a filesystem-safe slug and carries no meaning.
vault_slot: StringThe fob vault slot in this camp holding the plaintext value. Read by
yah cloud secret put at ship time and never recorded anywhere else — in
particular the value is not in this file, so the declaration is safe to
commit.
description: Option<String>Human note for yah cloud secret ls. What this secret is and who minted
it — the thing nobody remembers 6 months later.
encoding: SecretEncodingHow the vault slot’s text decodes into the bytes the consumer expects.
fob slots hold strings, but plenty of real secrets are binary — an
Ed25519 key is exactly 32 raw bytes, and yah-cloud-admin rejects a key
file of any other length. Without this field the only way to ship such a
key would be to hope its bytes happened to be valid UTF-8, which for a
random key they are not.
Defaults to SecretEncoding::Utf8 — the right answer for tokens,
passwords, and PEM, which is most secrets.
access: SecretAccessWho may mount it. Stamped onto the raft record verbatim.
Defaults to SecretAccess::default — the deny-all empty allow-list. A
declaration that forgets this field produces a secret nobody can mount,
which is the correct direction to fail in.
Three forms:
access = "allow_any" # explicit escape hatch
[access] # named workloads
workloads = [{ workload = "yah-cloud-admin" }]
[access] # signed recipes (R555-F5)
recipes = [{ recipe = "rusty-v8-musl", key = "3d40…" }]Use the recipes form for a credential a dispatched build needs (the
R2 write key, the cosign signing key). A remote QED run’s workload name
is a fresh forge-<uuid> every time, so workloads cannot name it and
allow_any over-answers — see W235 §Seam (c) secret scoping. key is
the hex Ed25519 public key from the recipe’s [admission] block.
target: Option<SecretTargetDecl>Advisory: the mount shape a consuming workload should declare. Not
enforced — yubaba honours whatever the WorkloadSpec asks for — but it
lets yah cloud secret put print the exact SecretMount to paste, so
the consumer and the declaration can’t drift on path or mode.
Implementations§
Source§impl SecretConfig
impl SecretConfig
Sourcepub fn load_dir(dir: &Path) -> Result<BTreeMap<String, Self>>
pub fn load_dir(dir: &Path) -> Result<BTreeMap<String, Self>>
Load every declaration in dir, keyed by logical secret name. A missing
directory is an empty map (a camp with no cluster secrets is normal).
Two files declaring the same name is a hard error, not a last-writer-
wins merge: they would race to define the access rule for one record, and
whichever lost would look correct in git while being inert on the fleet.
Trait Implementations§
Source§impl Clone for SecretConfig
impl Clone for SecretConfig
Source§fn clone(&self) -> SecretConfig
fn clone(&self) -> SecretConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for SecretConfig
impl Debug for SecretConfig
Source§impl<'de> Deserialize<'de> for SecretConfig
impl<'de> Deserialize<'de> for SecretConfig
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for SecretConfig
impl RefUnwindSafe for SecretConfig
impl Send for SecretConfig
impl Sync for SecretConfig
impl Unpin for SecretConfig
impl UnsafeUnpin for SecretConfig
impl UnwindSafe for SecretConfig
Blanket Implementations§
impl<T> Allocation for T
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<T> ErasedDestructor for Twhere
T: 'static,
impl<T> Fruit for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more