Skip to main content

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}