objectiveai_cli/filesystem/config/viewer.rs
1use serde::{Deserialize, Serialize};
2
3/// A generated secret/signature pair for viewer authentication.
4///
5/// The viewer server stores the `secret`. The API client stores the `signature`.
6/// The signature is `sha256=<hex of SHA256(secret)>`. The viewer server validates
7/// by computing SHA256(secret) and comparing against the incoming header value.
8/// Knowing the signature does not reveal the secret (preimage resistance).
9#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
10#[schemars(rename = "filesystem.config.ViewerSecretSignaturePair")]
11pub struct ViewerSecretSignaturePair {
12 /// The secret for the viewer server.
13 pub secret: String,
14 /// The pre-computed signature for the API client (`sha256=<hex>`).
15 pub signature: String,
16}
17
18/// Generates a random secret/signature pair for viewer authentication.
19///
20/// The secret is a random 64-character hex string. The signature is
21/// `sha256=<SHA256(secret)>` — a one-way derivation that cannot be reversed.
22pub fn generate_viewer_secret_signature_pair() -> ViewerSecretSignaturePair {
23 use sha2::{Digest, Sha256};
24
25 let mut bytes = [0u8; 32];
26 rand::fill(&mut bytes);
27 let secret: String = bytes.iter().map(|b| format!("{:02x}", b)).collect();
28
29 let hash = Sha256::digest(secret.as_bytes());
30 let signature = format!(
31 "sha256={}",
32 hash.iter()
33 .map(|b| format!("{:02x}", b))
34 .collect::<String>()
35 );
36
37 ViewerSecretSignaturePair { secret, signature }
38}