agentmux_srv/backend/agent_session/zone_naming.rs
1// Copyright 2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Zone-name validation + builders for agent session zones.
5
6/// Returns true if `s` matches `[A-Za-z0-9_-]+`. Rejects empty.
7///
8/// We're embedding `definition_id` into a zone name (a string the
9/// frontend can supply via RPC), so anything outside the safe set would
10/// let an attacker write/read arbitrary zones. UUIDs (the production
11/// definition_id shape) are a strict subset of this character class.
12pub fn is_valid_definition_id(s: &str) -> bool {
13 if s.is_empty() {
14 return false;
15 }
16 s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
17}
18
19/// `agent:<definition_id>:current`. Panics in debug if `definition_id`
20/// is invalid; callers should check `is_valid_definition_id` first (or use
21/// `validate_and_current`) in release.
22pub fn agent_current_zone(definition_id: &str) -> String {
23 debug_assert!(
24 is_valid_definition_id(definition_id),
25 "agent_current_zone: invalid definition_id"
26 );
27 format!("agent:{}:current", definition_id)
28}
29
30/// `agent:<definition_id>:archive:<ts_ms>`.
31pub fn agent_archive_zone(definition_id: &str, ts_ms: u64) -> String {
32 debug_assert!(
33 is_valid_definition_id(definition_id),
34 "agent_archive_zone: invalid definition_id"
35 );
36 format!("agent:{}:archive:{}", definition_id, ts_ms)
37}
38
39/// Convenience: validate + build the current-zone string. Returns
40/// `Err` with a stable error prefix on bad input so RPC callers see a
41/// consistent message.
42pub fn validate_and_current(definition_id: &str) -> Result<String, String> {
43 if !is_valid_definition_id(definition_id) {
44 return Err(format!(
45 "INVALID_DEFINITION_ID: must match [A-Za-z0-9_-]+, got {:?}",
46 definition_id
47 ));
48 }
49 Ok(agent_current_zone(definition_id))
50}