netget-tool 0.3.5

Network protocol stubbing toolkit: ~280 protocol servers and clients whose responses come from an LLM, a script, a static rule or a person.
use crate::protocol::log_template::LogTemplate;
use crate::{
    llm::actions::{
        client_trait::{Client, ClientActionResult},
        protocol_trait::Protocol,
        ActionDefinition, Parameter, ParameterDefinition,
    },
    protocol::{ConnectContext, EventType},
    state::AppState,
};
use anyhow::{bail, Result};
use serde_json::{json, Value};
use std::sync::LazyLock;
#[derive(Default)]
pub struct VaultClientProtocol;
impl VaultClientProtocol {
    pub fn new() -> Self {
        Self
    }
}
fn p(name: &str, kind: &str, description: &str, required: bool) -> Parameter {
    Parameter {
        name: name.into(),
        type_hint: kind.into(),
        description: description.into(),
        required,
    }
}
fn request() -> ActionDefinition {
    ActionDefinition{name:"vault_request".into(),description:"Selected Vault system reads and KV v2 read/write/list/metadata operations. No raw HTTP routes, dynamic-secret engines or administration.".into(),parameters:vec![p("operation","string","seal_status, health, leader, read, write, list, or metadata",true),p("mount","string","KV v2 mount override; default startup kv_mount",false),p("path","string","KV relative ASCII name segments; list accepts a folder slash; empty only for mount-root list",false),p("version","integer","read: nonnegative version; omitted/0 reads latest",false),p("data","object","write: secret object, encoded into native KV v2 data envelope",false),p("cas","integer","write: expected current version;0 creates only when no versions exist",false)],example:json!({"type":"vault_request","operation":"read","path":"fixture/app","version":1}),log_template:Some(LogTemplate::new().with_info("Vault {operation} request queued"))}
}
fn login() -> ActionDefinition {
    ActionDefinition{name:"vault_userpass_login".into(),description:"Authenticate selected userpass credentials. A new attempt discards the previous session token; only a complete validated success installs one token. Login password and returned token are omitted from events and incidental diagnostics.".into(),parameters:vec![p("username","string","Bounded ASCII userpass username",true),p("password","string","Userpass password; not included in result metadata",true),p("auth_mount","string","Userpass mount override; default startup auth_mount",false)],example:json!({"type":"vault_userpass_login","username":"fixture-reader","password":"fixture-password"}),log_template:Some(LogTemplate::new().with_info("Vault userpass authentication queued"))}
}
fn clear() -> ActionDefinition {
    ActionDefinition{name:"vault_clear_token".into(),description:"Forget this client's credential locally. Does not revoke a Vault token or end any backend lease.".into(),parameters:vec![],example:json!({"type":"vault_clear_token"}),log_template:Some(LogTemplate::new().with_info("Vault local credential cleared"))}
}
fn disconnect() -> ActionDefinition {
    ActionDefinition {
        name: "disconnect".into(),
        description: "Cancel pending I/O and stop this logical client".into(),
        parameters: vec![],
        example: json!({"type":"disconnect"}),
        log_template: Some(LogTemplate::new().with_info("Vault client disconnected")),
    }
}
fn actions() -> Vec<ActionDefinition> {
    vec![request(), login(), clear(), disconnect()]
}
fn event(id: &str, description: &str, params: Vec<Parameter>) -> EventType {
    EventType::new(id, description, request().example)
        .with_parameters(params)
        .with_actions(actions())
}
pub static CONNECTED_EVENT: LazyLock<EventType> = LazyLock::new(|| {
    event(
        "vault_connected",
        "Public seal status succeeded; configured token has not been verified",
        vec![
            p(
                "origin",
                "string",
                "Vault service HTTP or HTTPS origin",
                true,
            ),
            p("kv_mount", "string", "Default KV v2 mount", true),
            p("auth_mount", "string", "Default userpass mount", true),
            p(
                "token_present",
                "boolean",
                "Credential configured, not proof that Vault accepts it",
                true,
            ),
            p(
                "authentication_verified",
                "boolean",
                "False: startup seal probe is public",
                true,
            ),
            p(
                "seal",
                "object",
                "Typed seal status, including initialized/sealed/threshold/progress",
                true,
            ),
        ],
    )
});
pub static RESPONSE_EVENT: LazyLock<EventType> = LazyLock::new(|| {
    event("vault_response","Complete selected Vault response; legitimate health status codes preserve their native meaning",vec![p("request","object","Originating typed action with credentials omitted",true),p("operation","string","Selected operation",true),p("status","integer","HTTP status; health may return200/429/472/473/474/501/503/530",true),p("data","object","Typed system body or KV v2 envelope. Reads keep data and version metadata distinct; writes return positive version acknowledgement; lists return keys with folder suffixes; metadata retains versions, deletion/destruction state and RFC3339 times. No secret store is created.",true)])
});
pub static AUTH_EVENT: LazyLock<EventType> = LazyLock::new(|| {
    event("vault_authentication","Validated userpass success or explicit local credential clear",vec![p("request","object","Login receipt excludes password; clear receipt has no credential",true),p("operation","string","login or clear_token",true),p("status","integer|null","HTTP200 for login;null for local clear",true),p("token_present","boolean","One ephemeral credential currently retained",true),p("authentication_verified","boolean","True only after complete validated login auth; false for clear",true),p("auth","object|null","Token accessor, policies, metadata, lease_duration seconds, renewable, entity_id, service/batch token_type and optional orphan/num_uses. client_token and MFA challenge are never exposed; MFA continuation is refused.",true),p("request_id","string","Native login request identifier; absent for local clear",false),p("warnings","array|null","Credential-redacted login warnings; absent for local clear",false)])
});
pub static ERROR_EVENT: LazyLock<EventType> = LazyLock::new(|| {
    event(
        "vault_request_error",
        "Request refused or failed; no partial success, session remains available",
        vec![
            p(
                "request",
                "object",
                "Originating action with password omitted",
                true,
            ),
            p(
                "status",
                "integer|null",
                "Native HTTP status when received",
                true,
            ),
            p("category", "string", "http, schema, or transport", true),
            p(
                "errors",
                "array",
                "Credential-redacted native/validation/deadline diagnostics",
                true,
            ),
            p(
                "token_present",
                "boolean",
                "Current credential presence; failed login leaves it false",
                true,
            ),
        ],
    )
});
impl Protocol for VaultClientProtocol {
    fn protocol_name(&self) -> &'static str {
        "Vault"
    }
    fn stack_name(&self) -> &'static str {
        "ETH>IP>TCP>HTTP>Vault"
    }
    fn keywords(&self) -> Vec<&'static str> {
        vec!["vault", "hashicorp", "kv v2"]
    }
    fn description(&self) -> &'static str {
        "Selected typed Vault authentication and KV v2 operations"
    }
    fn group_name(&self) -> &'static str {
        "AI & API"
    }
    fn example_prompt(&self) -> &'static str {
        "Read version1 of fixture/app from Vault at localhost:8200"
    }
    fn get_async_actions(&self, _: &AppState) -> Vec<ActionDefinition> {
        actions()
    }
    fn get_sync_actions(&self) -> Vec<ActionDefinition> {
        vec![]
    }
    fn get_event_types(&self) -> Vec<EventType> {
        vec![
            CONNECTED_EVENT.clone(),
            RESPONSE_EVENT.clone(),
            AUTH_EVENT.clone(),
            ERROR_EVENT.clone(),
        ]
    }
    fn get_startup_parameters(&self) -> Vec<ParameterDefinition> {
        vec![
        ParameterDefinition{name:"token".into(),type_hint:"string".into(),description:"Optional bounded Vault token; syntax checked, never authenticated by the public startup probe. Ephemeral header only, no credential file.".into(),required:false,example:json!("hvs.fixture-token"),default:None},
        ParameterDefinition{name:"kv_mount".into(),type_hint:"string".into(),description:"Default KV v2 mount of bounded ASCII segments; sys is refused".into(),required:false,example:json!("secret"),default:Some(json!(super::DEFAULT_KV_MOUNT))},
        ParameterDefinition{name:"auth_mount".into(),type_hint:"string".into(),description:"Default selected userpass mount, excluding sys".into(),required:false,example:json!("userpass"),default:Some(json!(super::DEFAULT_AUTH_MOUNT))},
        ParameterDefinition{name:"request_timeout_secs".into(),type_hint:"integer".into(),description:"Whole request connect/head/body deadline1..30 seconds".into(),required:false,example:json!(10),default:Some(json!(super::DEFAULT_TIMEOUT_SECS))},
    ]
    }
    fn metadata(&self) -> crate::protocol::metadata::ProtocolMetadataV2 {
        use crate::protocol::metadata::*;
        ProtocolMetadataV2::builder().state(DevelopmentState::Experimental).privilege_requirement(PrivilegeRequirement::None).well_known_port(8200).implementation("Bounded HTTP(S), typed public system probes, KV v2 version/CAS/metadata and selected userpass login").llm_control("Selected native authentication and secret operations, redacted auth metadata, event routing/injection and shared memory").e2e_testing("tests/client/vault: native isolated Vault daemon and CLI, programmable NetGet pair, handler paths, schema/bounds/deadlines/cancellation; existing server native CLI tests").notes("One ephemeral token and one latest bounded password for reflection redaction, no protocol secret database. Body1MiB, arrays10000, object fields256, strings16KiB, depth32/nodes65536; in-memory actions preflight retained content8MiB before copying; one request, queues8,4 followups. HTTP(S) native with certificate verification; browser HTTP only. No custom trust, client certificates, namespaces, redirects, proxies, cookie state, token helpers/files, automatic auth/renewal/retry, response wrapping, MFA, OAuth/OIDC/AppRole, dynamic-secret engines, deletion/patch/undelete/destroy or administration. Clear is local, never backend revocation. Public seal probe does not verify startup tokens. Existing programmable KV v2 server auth/login refusal remains.").max_inbound_bytes(super::api::MAX_BODY).build()
    }
    fn get_startup_examples(&self) -> crate::llm::actions::StartupExamples {
        let llm = json!({"type":"open_client","base_stack":"vault","protocol":"vault","remote_addr":"127.0.0.1:8200","instruction":"Read Vault health, then explain initialized, sealed and standby status"});
        let mut fixed = llm.clone();
        fixed["event_handlers"] = json!([{"event_pattern":"vault_connected","handler":{"type":"static","actions":[{"type":"vault_request","operation":"health"}]}},{"event_pattern":"*","handler":{"type":"static","actions":[]}}]);
        let mut script = fixed.clone();
        script["event_handlers"][0]["handler"] = json!({"type":"script","language":"python","code":"import json,sys\njson.dump({'actions':[{'type':'vault_request','operation':'health'}]},sys.stdout)"});
        crate::llm::actions::StartupExamples::new(llm, script, fixed)
    }
}
impl Client for VaultClientProtocol {
    fn connect(
        &self,
        ctx: ConnectContext,
    ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<std::net::SocketAddr>> + Send>>
    {
        Box::pin(super::connect(ctx))
    }
    fn execute_action(&self, value: Value) -> Result<ClientActionResult> {
        if !super::api::action_within_budget(&value) {
            crate::utils::json_budget::drop_iteratively(value);
            bail!("Vault action depth/node/retained-content limit");
        }
        match value["type"].as_str() {
            Some("vault_request" | "vault_userpass_login" | "vault_clear_token") => {
                super::api::request(&value, super::DEFAULT_KV_MOUNT, super::DEFAULT_AUTH_MOUNT)?;
                Ok(ClientActionResult::Custom {
                    name: "vault_request".into(),
                    data: value,
                })
            }
            Some("disconnect") => Ok(ClientActionResult::Disconnect),
            _ => bail!("unknown Vault client action"),
        }
    }
}