Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
315 changes: 315 additions & 0 deletions docs/rfcs/0025-secrets.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions rust/crates/truapi-codegen/tests/golden/wire_table.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions rust/crates/truapi-server/src/generated/wire_table.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions rust/crates/truapi/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod payment;
pub mod permissions;
pub mod preimage;
pub mod resource_allocation;
pub mod secrets;
pub mod signing;
pub mod statement_store;
pub mod system;
Expand All @@ -27,6 +28,7 @@ pub use payment::Payment;
pub use permissions::Permissions;
pub use preimage::Preimage;
pub use resource_allocation::ResourceAllocation;
pub use secrets::Secrets;
pub use signing::Signing;
pub use statement_store::StatementStore;
pub use system::System;
Expand Down
35 changes: 35 additions & 0 deletions rust/crates/truapi/src/api/secrets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Unified [`Secrets`] trait.

use crate::versioned::secrets::{HostSecretError, HostSecretRequest, HostSecretResponse};
use crate::wire;
use crate::{CallContext, CallError};

/// Calls made with a credential the product never holds.
#[crate::async_trait]
pub trait Secrets: Send + Sync {
/// Send a request to a backend and return its response.
///
/// The backend is resolved as `secret:<name>` in the dotNS records of
/// `product_id`. That record fixes the endpoint, path, and method, so the
/// caller supplies only a query, headers, and a body.
///
/// ```ts
/// const result = await truapi.secrets.request({
/// productId: "onramp.dot",
/// name: "meld-session",
/// query: [],
/// headers: [{ name: "Content-Type", value: "application/json" }],
/// body: encoded,
/// });
/// assert(result.isOk(), "secrets.request failed:", result);
/// console.log("backend responded:", result.value.status);
/// ```
#[wire(request_id = 166)]
async fn request(
&self,
_cx: &CallContext,
_request: HostSecretRequest,
) -> Result<HostSecretResponse, CallError<HostSecretError>> {
Err(CallError::unavailable())
}
}
4 changes: 2 additions & 2 deletions rust/crates/truapi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ pub mod latest {
RemoteStatementStoreCreateProofError, RemoteStatementStoreCreateProofRequest,
RemoteStatementStoreCreateProofResponse, RemoteStatementStoreSubscribeItem,
RemoteStatementStoreSubscribeRequest, RingLocation, RuntimeApi, RuntimeSpec, RuntimeType,
SignedStatement, Statement, StatementProof, StorageQueryItem, StorageQueryType,
StorageResultItem, ThemeVariant, TxPayloadExtension,
SecretHeader, SecretQueryParam, SignedStatement, Statement, StatementProof,
StorageQueryItem, StorageQueryType, StorageResultItem, ThemeVariant, TxPayloadExtension,
};

/// Latest payload type of a versioned envelope.
Expand Down
2 changes: 2 additions & 0 deletions rust/crates/truapi/src/v01.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod payment;
mod permissions;
mod preimage;
mod resource_allocation;
mod secrets;
mod signing;
mod statement_store;
mod system;
Expand All @@ -30,6 +31,7 @@ pub use payment::*;
pub use permissions::*;
pub use preimage::*;
pub use resource_allocation::*;
pub use secrets::*;
pub use signing::*;
pub use statement_store::*;
pub use system::*;
Expand Down
77 changes: 77 additions & 0 deletions rust/crates/truapi/src/v01/secrets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use parity_scale_codec::{Decode, Encode};

/// Error from [`crate::api::Secrets::request`] (RFC 0025).
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub enum HostSecretError {
/// No authenticated session (RFC 0009). The host must not auto-prompt login.
NotConnected,
/// No record under that name.
UnknownSecret,
/// The record resolved but does not parse, or names an unsupported field.
MalformedRecord,
/// The user declined consent or the signing confirmation.
Rejected,
/// The user is not a people-set member, so no caller proof can be produced.
NotMember,
/// The backend could not be reached.
Transport,
/// The response exceeded the limit set by the host and was discarded.
ResponseTooLarge,
/// The request body or header count exceeded the limit set by the host.
RequestTooLarge,
/// Catch-all.
Unknown {
/// Human-readable failure reason.
reason: String,
},
}

/// One header on the outbound request.
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct SecretHeader {
/// Header name.
pub name: String,
/// Header value.
pub value: String,
}

/// One query parameter appended to the fixed path in the record.
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct SecretQueryParam {
/// Parameter name.
pub name: String,
/// Parameter value.
pub value: String,
}

/// Request to a backend holding a credential the product never sees (RFC 0025).
///
/// The backend is resolved as `secret:<name>` in the dotNS records of `product_id`.
/// That record fixes the endpoint, path, and method, so the caller supplies
/// only a query, headers, and a body. The host attaches a ring VRF proof over
/// the canonical digest, plus the contextual alias for that backend.
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct HostSecretRequest {
/// dotNS name whose records declare the backend. Often the calling product,
/// but naming another is how a product reaches a shared service.
pub product_id: String,
/// Secret name, resolved as `secret:<name>` in those records.
pub name: String,
/// Appended to the fixed path as a query string.
pub query: Vec<SecretQueryParam>,
/// Headers to forward. The host strips any in the `X-Polkadot-` namespace.
pub headers: Vec<SecretHeader>,
/// Request body, if the declared method takes one.
pub body: Option<Vec<u8>>,
}

/// Response returned by the backend, unmodified except for hop-by-hop headers.
#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)]
pub struct HostSecretResponse {
/// HTTP status the backend returned.
pub status: u16,
/// Response headers.
pub headers: Vec<SecretHeader>,
/// Response body.
pub body: Vec<u8>,
}
1 change: 1 addition & 0 deletions rust/crates/truapi/src/versioned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub mod payment;
pub mod permissions;
pub mod preimage;
pub mod resource_allocation;
pub mod secrets;
pub mod signing;
pub mod statement_store;
pub mod system;
Expand Down
9 changes: 9 additions & 0 deletions rust/crates/truapi/src/versioned/secrets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! Versioned wrappers for [`Secrets`](crate::api::Secrets) methods.

use crate::v01;

truapi_macros::versioned_type! {
pub enum HostSecretRequest { V1 => v01::HostSecretRequest }
pub enum HostSecretResponse { V1 => v01::HostSecretResponse }
pub enum HostSecretError { V1 => v01::HostSecretError }
}