Hmbown/CodeWhale · error
outbound origin must be a public service host
Error message
outbound origin must be a public service host
What it means
The validator requires the origin host to be a public service: names ending in .local (mDNS) or .internal (private DNS zones) are rejected because they only resolve on internal networks and defeat the public-host guarantee for credential-bearing calls.
Solutions
- Use the service's public hostname behind your gateway/proxy instead of the .local/.internal name.
- Expose the sandbox service via a public https endpoint (or tunnel) and configure that origin.
- For purely local testing, use debug builds with localhost rather than .internal names.
Example fix
// before export DAYTONA_API_URL=https://daytona.internal // after export DAYTONA_API_URL=https://daytona.mycompany.example.com
Defensive patterns
Strategy: validation
Validate before calling
let host = ... // extract host as in 765
if host.ends_with(".local") || host.ends_with(".internal") { return Err("use a public hostname"); } Try / catch
if let Err(e) = validate_outbound_origin(raw) {
if e.to_string().contains("public service host") {
eprintln!(".local/.internal hosts are rejected; expose the service publicly");
}
} Prevention
- Prefer public gateway hostnames over internal DNS names in client config.
- Document the public endpoint for self-hosted deployments.
- Validate origins at config load time so internal names fail early.
When it happens
Trigger: Setting the remote endpoint or toolbox URL to something like https://daytona.internal or https://toolbox.local.
Common situations: Corporate/internal cluster URLs pasted into client config; self-hosted deployments exposing only internal DNS names.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- outbound origin is empty or oversized
- outbound origin must be http or https
- agent profile provider cannot be empty
- agent profile provider must be a simple provider id
- api_key cannot be empty string
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/23ba7bd5fac8b64e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/cloud_dispatch.rs:1288
.host_str()
.context("outbound origin has no host")?
.trim_end_matches('.')
.to_ascii_lowercase();
// `Url::host_str` keeps IPv6 brackets; strip them for the checks below.
let host = host
.strip_prefix('[')
.and_then(|inner| inner.strip_suffix(']'))
.map(str::to_string)
.unwrap_or(host);
let loopback_name = host == "localhost" || host == "127.0.0.1" || host == "::1";
if loopback_name {
if cfg!(debug_assertions) {
return Ok(url);
}
bail!("loopback origins are not allowed in release builds");
}
if host.ends_with(".local") || host.ends_with(".internal") {
bail!("outbound origin must be a public service host");
}
if let Ok(ip) = host.parse::<std::net::IpAddr>() {
let blocked = match ip {
std::net::IpAddr::V4(v4) => {
let octets = v4.octets();
v4.is_loopback()
|| v4.is_private()
|| v4.is_link_local()
|| v4.is_unspecified()
|| v4.is_broadcast()
|| v4.is_multicast()
|| v4.is_documentation()
// 100.64.0.0/10 (carrier-grade NAT, `is_shared` is
// not stable yet)
|| (octets[0] == 100 && (octets[1] & 0b1100_0000) == 0b0100_0000)
}
std::net::IpAddr::V6(v6) => {
if let Some(v4) = v6.to_ipv4_mapped() {View on GitHub (pinned to 73e0f67d83)