The invariants in CLAUDE.md were tested functions nobody called. There was no config type at all: --config was accepted and ignored by all three binaries, figment was an unused dependency, and the 124-line config.toml.tmpl was aspirational. assert_not_anthropic could not run at startup because nothing read the provider it checks. Add tireless_core::config, validated on construction so a Config in hand is a checked one, and tireless_agent::preflight, called by the worker at startup and by `tireless preflight` on demand — the same function, so the two cannot disagree. Move the Anthropic guard to tireless_core::policy where validate() can reach it; tireless_agent::opencode re-exports it so the documented path resolves. Also make /v1/ready honest. It returned "ok" unconditionally while its own doc comment promised dependency checks, so the deploy probe greened on a process that could do nothing. It now reports per-dependency state, with unwired ones saying not_implemented rather than ok. Tests parse the shipped template rather than a fixture, so template and code cannot drift apart silently. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013TxK1CWPkFXqdcXMJ4hVe6
90 lines
3.4 KiB
Rust
90 lines
3.4 KiB
Rust
//! Constraints that are not ours to relax.
|
|
//!
|
|
//! Everything in this module encodes a term of service or a safety property
|
|
//! rather than a design preference. They live in core, next to the config that
|
|
//! carries the values they check, so that there is exactly one place a
|
|
//! configuration is admitted — see [`crate::config::Config::validate`].
|
|
//!
|
|
//! They were previously enforced by whichever binary remembered to call them,
|
|
//! which is a way of saying they were not enforced.
|
|
|
|
use tireless_entities::Error;
|
|
|
|
/// Provider identifiers that indicate an Anthropic backend.
|
|
const ANTHROPIC_MARKERS: &[&str] = &["anthropic", "claude"];
|
|
|
|
/// Refuse a configuration that points the OpenCode lane at Anthropic.
|
|
///
|
|
/// OpenCode is a third-party harness with its own provider clients. Driving an
|
|
/// Anthropic *subscription* through one is the pattern Anthropic blocked in
|
|
/// January 2026, and OpenCode was named explicitly. Anthropic work goes through
|
|
/// the Claude Code lane, which spawns the first-party binary and lets it
|
|
/// authenticate itself.
|
|
///
|
|
/// Checked on both the provider id and the base URL, because either can carry
|
|
/// the intent. This is a terms-of-service constraint expressed as code: the
|
|
/// comment explaining it can rot, a failing startup assertion cannot.
|
|
pub fn assert_not_anthropic(provider: &str, base_url: &str) -> Result<(), Error> {
|
|
let provider_lc = provider.to_ascii_lowercase();
|
|
let url_lc = base_url.to_ascii_lowercase();
|
|
|
|
// Match provider ids on token boundaries so a self-hosted provider that
|
|
// merely *serves* a Claude-compatible surface is not caught by accident,
|
|
// while `anthropic`, `anthropic-oauth` and `claude-max` all are.
|
|
let provider_hits = ANTHROPIC_MARKERS.iter().any(|m| {
|
|
provider_lc
|
|
.split(|c: char| !c.is_ascii_alphanumeric())
|
|
.any(|part| part == *m)
|
|
});
|
|
|
|
// The URL check is host-based: `api.anthropic.com` is disqualifying wherever
|
|
// it appears, but a local gateway is not.
|
|
let url_hits = url_lc.contains("anthropic.com");
|
|
|
|
if provider_hits || url_hits {
|
|
return Err(Error::AnthropicViaOpencode(provider.to_string()));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn the_house_helexa_backend_is_accepted() {
|
|
assert!(assert_not_anthropic("lair-helexa", "http://hanzalova.internal:31313/v1").is_ok());
|
|
assert!(
|
|
assert_not_anthropic("lmstudio", "http://beast.hanzalova.internal:1234/v1").is_ok()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn anthropic_providers_are_refused() {
|
|
for provider in [
|
|
"anthropic",
|
|
"Anthropic",
|
|
"anthropic-oauth",
|
|
"claude",
|
|
"claude-max",
|
|
] {
|
|
assert!(
|
|
assert_not_anthropic(provider, "http://localhost/v1").is_err(),
|
|
"{provider} should be refused"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn the_anthropic_api_host_is_refused_whatever_the_provider_is_called() {
|
|
assert!(assert_not_anthropic("totally-fine", "https://api.anthropic.com/v1").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn a_local_gateway_serving_an_anthropic_compatible_surface_is_fine() {
|
|
// cortex presents both OpenAI and Anthropic compatible APIs, but it is
|
|
// local inference — no Anthropic subscription is involved.
|
|
assert!(assert_not_anthropic("lair-helexa", "http://hanzalova.internal:31313/v1").is_ok());
|
|
}
|
|
}
|