Skip to main content

golem_schema/schema/
schema_value.rs

1// Copyright 2024-2026 Golem Cloud
2//
3// Licensed under the Golem Source License v1.1 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::model::EnvironmentId;
16use crate::schema::schema_type::QuantityValue;
17use crate::schema::stream::SchemaValueStream;
18use chrono::{DateTime, Utc};
19use golem_schema_derive::{FromSchema, IntoSchema};
20use serde::{Deserialize, Serialize};
21
22/// The payload carried by [`SchemaValue::Secret`].
23///
24/// Like quota tokens, secrets are opaque capabilities. On the host (and in
25/// feature-neutral builds) the value is a trusted snapshot that contains only
26/// stable identity and metadata; plaintext is never stored here. On a guest it
27/// is an opaque, affine, take-once owned handle.
28#[cfg(not(all(feature = "guest", not(feature = "host"))))]
29pub type SecretVariantValue = SecretValuePayload;
30
31/// The payload carried by [`SchemaValue::Secret`] on a guest: an opaque,
32/// affine owned handle. See [`SecretVariantValue`] (host build) for details.
33#[cfg(all(feature = "guest", not(feature = "host")))]
34pub type SecretVariantValue = crate::schema::wit::GuestSecretHandle;
35
36/// The payload carried by [`SchemaValue::QuotaToken`].
37///
38/// A quota-token is an opaque, unforgeable capability. The representation
39/// differs by build target so that the value can never be inspected or
40/// fabricated by a guest:
41///
42/// - On the host (and in feature-neutral builds) it is the trusted internal
43///   snapshot [`QuotaTokenValuePayload`], converted to/from an owned
44///   `quota-token` handle by a `QuotaTokenResolver` at the WIT boundary.
45/// - On a guest it is an opaque, affine, take-once owned handle
46///   ([`crate::schema::wit::GuestQuotaTokenHandle`]) that the guest can only
47///   hold and transfer, never read.
48#[cfg(not(all(feature = "guest", not(feature = "host"))))]
49pub type QuotaTokenVariantValue = QuotaTokenValuePayload;
50
51/// The payload carried by [`SchemaValue::QuotaToken`] on a guest: an opaque,
52/// affine owned handle. See [`QuotaTokenVariantValue`] (host build) for details.
53#[cfg(all(feature = "guest", not(feature = "host")))]
54pub type QuotaTokenVariantValue = crate::schema::wit::GuestQuotaTokenHandle;
55
56/// The payload carried by [`SchemaValue::PermissionCard`].
57///
58/// A permission-card is an opaque, unforgeable capability, exactly like a
59/// secret or quota-token. The representation differs by build target:
60///
61/// - On the host (and in feature-neutral builds) it is the trusted internal
62///   snapshot [`PermissionCardValuePayload`], converted to/from an owned
63///   `permission-card` handle by a `PermissionCardResolver` at the WIT
64///   boundary. The `card_id` field is the only authoritative identity; the
65///   other fields are trusted cache verified against the card store.
66/// - On a guest it is an opaque, affine, take-once owned handle
67///   ([`crate::schema::wit::GuestPermissionCardHandle`]) that the guest can
68///   only hold and transfer, never read.
69#[cfg(not(all(feature = "guest", not(feature = "host"))))]
70pub type PermissionCardVariantValue = PermissionCardValuePayload;
71
72/// The payload carried by [`SchemaValue::PermissionCard`] on a guest: an
73/// opaque, affine owned handle. See [`PermissionCardVariantValue`] (host
74/// build) for details.
75#[cfg(all(feature = "guest", not(feature = "host")))]
76pub type PermissionCardVariantValue = crate::schema::wit::GuestPermissionCardHandle;
77
78/// One node in the recursive in-memory schema-value tree.
79///
80/// Always travels paired with a [`super::SchemaGraph`] (see
81/// [`super::TypedSchemaValue`]). The value tree is structurally driven by the
82/// schema: record-value payload order matches the schema's field order,
83/// variant-value carries a case index, enum-value carries a case index,
84/// union-value carries the discriminator's literal tag. The value side does
85/// not redundantly carry field names, case names, or named-ref identifiers —
86/// those come from the schema.
87#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, IntoSchema, FromSchema)]
88#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
89#[cfg_attr(feature = "full", desert(evolution()))]
90#[serde(tag = "kind", content = "value", rename_all = "kebab-case")]
91#[schema(named = "schema-value")]
92#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
93pub enum SchemaValue {
94    // Primitives
95    Bool(bool),
96    S8(i8),
97    S16(i16),
98    S32(i32),
99    S64(i64),
100    U8(u8),
101    U16(u16),
102    U32(u32),
103    U64(u64),
104    F32(f32),
105    F64(f64),
106    Char(char),
107    String(String),
108
109    // Structural composites
110    Record {
111        fields: Vec<SchemaValue>,
112    },
113    Variant(VariantValuePayload),
114    Enum {
115        case: u32,
116    },
117    Flags {
118        bits: Vec<bool>,
119    },
120    Tuple {
121        elements: Vec<SchemaValue>,
122    },
123    List {
124        elements: Vec<SchemaValue>,
125    },
126    FixedList {
127        elements: Vec<SchemaValue>,
128    },
129    Map {
130        entries: Vec<(SchemaValue, SchemaValue)>,
131    },
132    Option {
133        inner: Option<Box<SchemaValue>>,
134    },
135    Result(ResultValuePayload),
136
137    // Rich semantic
138    Text(TextValuePayload),
139    Binary(BinaryValuePayload),
140    Path {
141        path: String,
142    },
143    Url {
144        url: String,
145    },
146    Uuid(uuid::Uuid),
147    Datetime {
148        value: DateTime<Utc>,
149    },
150    Duration(DurationValuePayload),
151    Quantity(QuantityValue),
152
153    // Discriminated union
154    Union(UnionValuePayload),
155
156    // Capability nodes
157    #[cfg_attr(all(feature = "guest", not(feature = "host")), serde(skip))]
158    #[cfg_attr(
159        all(feature = "full", feature = "guest", not(feature = "host")),
160        transient
161    )]
162    Secret(SecretVariantValue),
163    #[cfg_attr(all(feature = "guest", not(feature = "host")), serde(skip))]
164    #[cfg_attr(
165        all(feature = "full", feature = "guest", not(feature = "host")),
166        transient
167    )]
168    QuotaToken(QuotaTokenVariantValue),
169    PermissionCard(PermissionCardVariantValue),
170    #[serde(skip)]
171    #[cfg_attr(feature = "full", transient)]
172    Stream(SchemaValueStream),
173}
174
175#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, IntoSchema, FromSchema)]
176#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
177#[cfg_attr(feature = "full", desert(evolution()))]
178#[serde(rename_all = "camelCase")]
179#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
180pub struct VariantValuePayload {
181    pub case: u32,
182    #[serde(default, skip_serializing_if = "Option::is_none")]
183    pub payload: Option<Box<SchemaValue>>,
184}
185
186/// Result payload: exactly one of `Ok` / `Err` is set. Each inner option
187/// allows `result<_, _>` cases whose ok/err type is unit (no payload).
188#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, IntoSchema, FromSchema)]
189#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
190#[cfg_attr(feature = "full", desert(evolution()))]
191#[serde(tag = "tag", rename_all = "kebab-case")]
192#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
193pub enum ResultValuePayload {
194    Ok { value: Option<Box<SchemaValue>> },
195    Err { value: Option<Box<SchemaValue>> },
196}
197
198#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, IntoSchema, FromSchema)]
199#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
200#[cfg_attr(feature = "full", desert(evolution()))]
201#[serde(rename_all = "camelCase")]
202#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
203pub struct TextValuePayload {
204    pub text: String,
205    /// BCP-47 language tag, when known.
206    #[serde(default, skip_serializing_if = "Option::is_none")]
207    pub language: Option<String>,
208}
209
210#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, IntoSchema, FromSchema)]
211#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
212#[cfg_attr(feature = "full", desert(evolution()))]
213#[serde(rename_all = "camelCase")]
214#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
215pub struct BinaryValuePayload {
216    pub bytes: Vec<u8>,
217    #[serde(default, skip_serializing_if = "Option::is_none")]
218    pub mime_type: Option<String>,
219}
220
221/// Signed duration as total nanoseconds.
222#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, IntoSchema, FromSchema)]
223#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
224#[cfg_attr(feature = "full", desert(evolution()))]
225#[serde(rename_all = "camelCase")]
226#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
227pub struct DurationValuePayload {
228    pub nanoseconds: i64,
229}
230
231#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, IntoSchema, FromSchema)]
232#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
233#[cfg_attr(feature = "full", desert(evolution()))]
234#[serde(rename_all = "camelCase")]
235#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
236pub struct UnionValuePayload {
237    /// Tag of the branch the decoder resolved, matching one of the
238    /// [`super::UnionBranch::tag`] values. Carried so receivers do not have
239    /// to re-run discriminator rules to know which branch was matched;
240    /// encoders must ensure it agrees with the body.
241    pub tag: String,
242    /// Underlying value. Its shape matches the resolved branch's body type
243    /// and (by construction) satisfies the branch's discriminator rule.
244    pub body: Box<SchemaValue>,
245}
246
247/// Capability value: the trusted host snapshot of a secret handle.
248///
249/// This contains only identity and metadata needed to deterministically
250/// resurrect a handle. Plaintext secret material lives in the host resource
251/// representation / registry store and is never carried by `SchemaValue`.
252#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, IntoSchema, FromSchema)]
253#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
254#[cfg_attr(feature = "full", desert(evolution()))]
255#[serde(rename_all = "camelCase")]
256#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
257pub struct SecretValuePayload {
258    pub secret_id: uuid::Uuid,
259    #[serde(default, skip_serializing_if = "Option::is_none")]
260    pub config_key: Option<Vec<String>>,
261    pub version: u64,
262    pub resolved_at: DateTime<Utc>,
263    #[serde(default, skip_serializing_if = "Option::is_none")]
264    pub category: Option<String>,
265}
266
267/// Capability value: the trusted internal/persistent representation of a
268/// quota-token, held only inside `SchemaValue::QuotaToken`. Across a WIT
269/// boundary the token travels as an opaque, unforgeable owned handle
270/// (`quota-token-handle(own<quota-token>)`); the host converts between this
271/// snapshot and a handle through a resolver and the receiver re-acquires a live
272/// lease against `(environment_id, resource_name)` on demand. This snapshot is
273/// never exposed to or constructible by a guest.
274#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, IntoSchema, FromSchema)]
275#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
276#[cfg_attr(feature = "full", desert(evolution()))]
277#[serde(rename_all = "camelCase")]
278#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
279pub struct QuotaTokenValuePayload {
280    pub environment_id: EnvironmentId,
281    pub resource_name: String,
282    pub expected_use: u64,
283    pub last_credit: i64,
284    pub last_credit_at: DateTime<Utc>,
285}
286
287/// Capability value: the trusted internal/persistent representation of a
288/// permission-card, held only inside `SchemaValue::PermissionCard`. Across a
289/// WIT boundary the card travels as an opaque, unforgeable owned handle
290/// (`permission-card-handle(own<permission-card>)`); the host converts between
291/// this snapshot and a handle through a resolver and the receiver re-acquires
292/// the live card against `card_id` on demand. This snapshot is never exposed
293/// to or constructible by a guest.
294///
295/// Only `card_id` is authoritative identity. The remaining fields are trusted
296/// cache verified against the card store on the host side; receivers must not
297/// treat them as proof of authorization, only as a hint of the card's last
298/// known shape.
299#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, IntoSchema, FromSchema)]
300#[cfg_attr(feature = "full", derive(desert_rust::BinaryCodec))]
301#[cfg_attr(feature = "full", desert(evolution()))]
302#[serde(rename_all = "camelCase")]
303#[cfg_attr(feature = "full", derive(golem_schema_derive::PoemSchema))]
304pub struct PermissionCardValuePayload {
305    /// Authoritative identity of the card. Survives serialization and is the
306    /// only field a receiver may trust without re-validation.
307    pub card_id: uuid::Uuid,
308    /// Direct parents in the permission DAG. Order is not significant. Trusted
309    /// cache only; receivers re-validate by walking the live card store.
310    #[serde(default, skip_serializing_if = "Vec::is_empty")]
311    pub parent_ids: Vec<uuid::Uuid>,
312    /// Absolute expiry of the card, if any. Trusted cache only.
313    #[serde(default, skip_serializing_if = "Option::is_none")]
314    pub expires_at: Option<DateTime<Utc>>,
315    /// Whether the card is polymorphic (can spawn scoped child cards). Trusted
316    /// cache only; the live card store is the source of truth.
317    pub polymorphic: bool,
318}