capsule_runtime/spawn_credential.rs
1//! The two secrets a delegated spawn travels on, and the headers they travel in.
2//!
3//! `mur-roost` mints both; the runtime carries them and presents them. They live here, in the
4//! crate both sides already depend on, so the daemon and its one client read a single definition
5//! of each header name rather than two string literals that can drift apart.
6//!
7//! * A **credential** names the session that holds it. It is minted once, when a session whose
8//! manifest declares `capabilities.spawn.allow` is staged, and it is what makes a spawn request
9//! answerable at all — the daemon judges the session the credential names, never the one a
10//! request body claims.
11//! * An **approval** names one resolved artifact, for one session, for one launch. It is what
12//! `POST /delegate` returns and what `POST /spawn` redeems.
13//!
14//! Both are opaque to this crate. Neither type parses, compares or validates what it holds: the
15//! authority that minted a token is the only thing that can read it, and everything here exists to
16//! make the string hard to spill on the way there.
17//!
18//! **Neither implements `Display` or `Serialize`, and both print redacted under `Debug`.** A token
19//! reaches a trace, a log line or a tool result the moment one of those exists — the formatting
20//! call site does not have to be about the token for the token to end up in the record. Reading one
21//! takes the deliberate step of calling [`SpawnCredential::expose`] or [`SpawnApproval::expose`],
22//! and those have exactly one call site each: `delegation_plane::DelegationPlane::delegate`, which
23//! puts the credential in one request header, and `child_launch::launch_child_capsule`, which
24//! writes the approval to one closed pipe.
25
26/// Request header carrying the credential that names the calling session. Required on
27/// `POST /delegate` and on any `POST /spawn` that redeems an approval.
28pub const SPAWN_CREDENTIAL_HEADER: &str = "x-murmur-spawn-credential";
29
30/// Request header carrying the approval a `POST /delegate` returned. Required on any
31/// `POST /spawn` other than the operator's own top-level path.
32pub const SPAWN_APPROVAL_HEADER: &str = "x-murmur-spawn-approval";
33
34/// The opaque, per-session token that proves which session is asking.
35///
36/// Handed to a session's runtime at launch and held in memory for the life of that runtime. It is
37/// never written to the workdir, never placed in an environment variable, never returned in a tool
38/// result and never rendered into an error the model sees.
39#[derive(Clone)]
40pub struct SpawnCredential(String);
41
42impl SpawnCredential {
43 pub fn new(token: String) -> Self {
44 Self(token)
45 }
46
47 /// The token text, for placing in a request header and nothing else.
48 pub fn expose(&self) -> &str {
49 &self.0
50 }
51}
52
53impl std::fmt::Debug for SpawnCredential {
54 /// Prints no token material.
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 f.write_str("SpawnCredential(<redacted>)")
57 }
58}
59
60/// The opaque, single-use token that names one approved launch of one resolved artifact.
61///
62/// Bound to the session that earned it as well as to the artifact, so redeeming it requires
63/// re-presenting the credential alongside it.
64#[derive(Clone)]
65pub struct SpawnApproval(String);
66
67impl SpawnApproval {
68 pub fn new(token: String) -> Self {
69 Self(token)
70 }
71
72 /// The token text, for placing in a request header and nothing else.
73 pub fn expose(&self) -> &str {
74 &self.0
75 }
76}
77
78impl std::fmt::Debug for SpawnApproval {
79 /// Prints no token material.
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 f.write_str("SpawnApproval(<redacted>)")
82 }
83}