systemprompt_models/profile/governance.rs
1//! Governance configuration for the gateway + MCP authorization hook.
2//!
3//! Authz is **fail-closed** with an explicit-opt-in surface. Three modes:
4//!
5//! - `webhook` — production. Core POSTs every request to the configured URL;
6//! any transport error, non-2xx, or decode failure denies the request.
7//! - `disabled` — denies every request via `DenyAllHook`. Use when authz is
8//! intentionally inactive but you want the surface installed.
9//! - `unrestricted` — TEST/DEV ONLY. Allows every request via `AllowAllHook`.
10//! Requires `acknowledgement` to equal the literal sentence `"I understand
11//! this disables all authorization"`. Bootstrap errors otherwise.
12//!
13//! Absent `governance` block, absent `authz`, or any unparseable config →
14//! bootstrap installs `DenyAllHook` (everything denied) so misconfiguration
15//! never silently grants access.
16//!
17//! Example:
18//!
19//! ```yaml
20//! governance:
21//! authz:
22//! hook:
23//! mode: webhook
24//! url: http://localhost:8080/api/public/govern/authz
25//! timeout_ms: 500
26//! ```
27
28use serde::{Deserialize, Serialize};
29
30pub const UNRESTRICTED_ACKNOWLEDGEMENT: &str = "I understand this disables all authorization";
31
32#[derive(Debug, Clone, Default, Serialize, Deserialize)]
33pub struct GovernanceConfig {
34 #[serde(default)]
35 pub authz: Option<AuthzConfig>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct AuthzConfig {
40 pub hook: AuthzHookConfig,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "lowercase")]
45pub enum AuthzMode {
46 Webhook,
47 Disabled,
48 Unrestricted,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct AuthzHookConfig {
53 pub mode: AuthzMode,
54 #[serde(default)]
55 pub url: Option<String>,
56 #[serde(default = "default_timeout_ms")]
57 pub timeout_ms: u64,
58 #[serde(default)]
59 pub acknowledgement: Option<String>,
60}
61
62const fn default_timeout_ms() -> u64 {
63 500
64}