atd_runtime/ucan/types.rs
1//! UCAN-lite payload types.
2//!
3//! Decoded from a JWT compact form by [`crate::ucan::parse_jwt`]. Pure
4//! data — no behaviour, no allocations beyond the strings these fields
5//! own.
6//!
7//! Spec: `docs/archive/superpowers/specs/2026-05-11-sp-capability-v2-design.md` §4.1, §4.5, §5.1
8
9use serde::{Deserialize, Serialize};
10
11/// JWT header — the part before the first `.` in the compact form.
12///
13/// Spec §4.1 + §4.3: `alg` must be `EdDSA`, `typ` must be `ucan/1.0+jwt`,
14/// `ucv` must be `1.0`. Anything else is rejected at parse time.
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub struct UcanHeader {
17 pub alg: String,
18 pub typ: String,
19 pub ucv: String,
20}
21
22/// UCAN capability inside `payload.args` — a flat list of ATD capability
23/// strings (`records:read`, `fs.write`, ...) plus optional resource
24/// bindings (e.g. `{"patient": "Patient/X"}`).
25///
26/// Spec §4.5 — `with` reserved for future binding kinds; v1 supports
27/// `{"patient": "..."}` only.
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub struct UcanCapability {
30 pub caps: Vec<String>,
31 #[serde(default, skip_serializing_if = "Vec::is_empty")]
32 pub with: Vec<serde_json::Value>,
33}
34
35/// UCAN payload — the middle segment of the JWT compact form.
36///
37/// Spec §5.1: full canonical "A→B delegates read-only Patient/X access"
38/// example. `prf` carries parent UCAN(s) inline so verification stays
39/// self-contained (no out-of-band fetches).
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct UcanPayload {
42 /// Issuer DID (the principal granting authority).
43 pub iss: String,
44
45 /// Audience DID (the principal authorised to act).
46 pub aud: String,
47
48 /// Subject DID — the resource owner / root authority.
49 pub sub: String,
50
51 /// Reserved namespace sentinel; must be `"atd-cap"` for ATD-bound
52 /// tokens. Cross-system replay (e.g. a Bluesky UCAN) is structurally
53 /// prevented by this discriminator (spec §4.5).
54 pub cmd: String,
55
56 /// Capabilities + optional resource bindings.
57 pub args: UcanCapability,
58
59 /// 16-byte random nonce (base64url-encoded).
60 pub nonce: String,
61
62 /// Unix-seconds expiry. Verified against `SystemTime::now()` at
63 /// chain-walk time (Phase B.2).
64 pub exp: i64,
65
66 /// Parent UCANs (each itself a JWT compact form). Forms the
67 /// delegation chain. Empty for root UCANs signed by the resource
68 /// owner.
69 #[serde(default, skip_serializing_if = "Vec::is_empty")]
70 pub prf: Vec<String>,
71}