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, schemars::JsonSchema)]
33#[serde(deny_unknown_fields)]
34pub struct GovernanceConfig {
35 #[serde(default)]
36 pub authz: Option<AuthzConfig>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
40#[serde(deny_unknown_fields)]
41pub struct AuthzConfig {
42 pub hook: AuthzHookConfig,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
46#[serde(rename_all = "lowercase")]
47pub enum AuthzMode {
48 Webhook,
49 Disabled,
50 Unrestricted,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
54#[serde(deny_unknown_fields)]
55pub struct AuthzHookConfig {
56 pub mode: AuthzMode,
57 #[serde(default)]
58 pub url: Option<String>,
59 #[serde(default = "default_timeout_ms")]
60 pub timeout_ms: u64,
61 #[serde(default)]
62 pub acknowledgement: Option<String>,
63}
64
65const fn default_timeout_ms() -> u64 {
66 500
67}