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. Four 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//! - `extension` — production. The hook is supplied at bootstrap by the binary
8//! via `AppContextBuilder::with_authz_hook(...)`. Bootstrap errors if no hook
9//! is supplied. See `internal/guides/authz.md`.
10//! - `disabled` — denies every request via `DenyAllHook`. Use when authz is
11//! intentionally inactive but you want the surface installed.
12//! - `unrestricted` — TEST/DEV ONLY. Allows every request via `AllowAllHook`.
13//! Requires `acknowledgement` to equal the literal sentence `"I understand
14//! this disables all authorization"`. Bootstrap errors otherwise.
15//!
16//! Absent `governance` block, absent `authz`, or any unparseable config →
17//! bootstrap installs `DenyAllHook` (everything denied) so misconfiguration
18//! never silently grants access.
19//!
20//! Example:
21//!
22//! ```yaml
23//! governance:
24//! authz:
25//! hook:
26//! mode: webhook
27//! url: http://localhost:8080/api/public/govern/authz
28//! timeout_ms: 500
29//! ```
30//!
31//! Copyright (c) systemprompt.io — Business Source License 1.1.
32//! See <https://systemprompt.io> for licensing details.
33
34use serde::{Deserialize, Serialize};
35
36pub const UNRESTRICTED_ACKNOWLEDGEMENT: &str = "I understand this disables all authorization";
37
38#[derive(Debug, Clone, Default, Serialize, Deserialize, schemars::JsonSchema)]
39#[serde(deny_unknown_fields)]
40pub struct GovernanceConfig {
41 #[serde(default)]
42 pub authz: Option<AuthzConfig>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
46#[serde(deny_unknown_fields)]
47pub struct AuthzConfig {
48 pub hook: AuthzHookConfig,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
52#[serde(rename_all = "lowercase")]
53pub enum AuthzMode {
54 Webhook,
55 Extension,
56 Disabled,
57 Unrestricted,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
61#[serde(deny_unknown_fields)]
62pub struct AuthzHookConfig {
63 pub mode: AuthzMode,
64 #[serde(default)]
65 pub url: Option<String>,
66 #[serde(default = "default_timeout_ms")]
67 pub timeout_ms: u64,
68 #[serde(default)]
69 pub acknowledgement: Option<String>,
70}
71
72const fn default_timeout_ms() -> u64 {
73 500
74}