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
27 changes: 27 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,33 @@ memory_path = "~/.codewhale/memory.md"
# max_reprompts = 2
# reprompt_message = "So, what's up ? Keep running !"

# ───────────────────────────────────────────────────────────────────────────
# Model-bound key redaction ([redaction])
# ───────────────────────────────────────────────────────────────────────────
# Codewhale masks credential-looking values in tool output before it reaches
# the model (the "model boundary"), so a file that contains a configured API
# key, a bare provider token, or a credential-shaped opaque string never leaks
# those bytes to the model. Leave this enabled unless the model must read and
# edit files that contain real credentials.
#
# Disabling is a security decision, so it is never a plain flag:
# * Set model_bound = "disabled" here, restart Codewhale, and the startup
# gate asks twice - a first confirmation, then a red "are you really
# sure?" stage. Only the second confirmation takes effect, and it applies
# on later launches while model_bound stays "disabled".
# * Going back to "enabled" - or rewriting config.toml after the
# confirmation - invalidates it: requesting "disabled" again always
# asks for a fresh confirmation.
# * Until a confirmation exists - including in non-interactive/headless
# runs, which never confirm anything - masking stays on regardless of
# this key. Choosing "keep masking on" on the gate leaves the key
# untouched, so the next launch asks again.
# * The value is forgiving: false/"off" mean "disabled"; true/"on" mean
# "enabled".
# [redaction]
# model_bound = "enabled" # mask keys before they reach the model (default)
# model_bound = "disabled" # request the opt-out (restart + confirm required)

# Native tool catalog controls (#2076). By default only the core tool surface
# is loaded into the model context; less common native tools are discoverable
# through ToolSearch and loaded on first use.
Expand Down
20 changes: 20 additions & 0 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod provider;
mod provider_defaults;
mod provider_kind;
pub mod provider_templates;
pub mod redaction;
pub mod resolve;
pub mod route;
pub mod settings_schema;
Expand Down Expand Up @@ -920,10 +921,29 @@ pub struct ConfigToml {
/// [`WorkflowConfigToml::default`].
#[serde(default)]
pub workflow: Option<WorkflowConfigToml>,
/// Model-bound credential redaction policy (`[redaction]`). When absent,
/// masking is enabled — the shipped security default.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub redaction: Option<crate::redaction::RedactionToml>,
#[serde(flatten)]
pub extras: BTreeMap<String, toml::Value>,
}

impl ConfigToml {
/// The requested model-bound masking mode, defaulting to enabled.
///
/// The request only takes effect once the interactive TUI has recorded a
/// confirmation on its startup gate; see
/// [`crate::redaction::effective_masking`].
#[must_use]
pub fn redaction_model_bound_masking(&self) -> crate::redaction::ModelBoundMasking {
self.redaction
.as_ref()
.map(crate::redaction::RedactionToml::model_bound_masking)
.unwrap_or_default()
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ProviderConfigField {
ApiKey,
Expand Down
Loading
Loading