use geth_auth::{AuthExplanation, AuthOp, AuthOpSignature}; use geth_cas::{FileConflict, FileRoot, FileRootScan}; use geth_db::{CrSqliteChangeBatch, DbResource}; use geth_discovery::{DiscoveredPeer, PeerCard}; use geth_document::{DocumentResource, DocumentState}; use geth_keychain::{ KeychainAllowedSigner, KeychainCheckpoint, KeychainOp, KeychainOpSignature, KeychainSigchainEntry, KeychainSigchainReport, NodeEnrollmentRequest, NodeRecord, }; use geth_kv::{KvEntry, KvResource, KvSyncEntry}; use geth_overlay::{ OverlayInterfacePlan, OverlayJoinPlan, OverlayNetworkStatus, OverlayPacket, OverlayPlan, OverlayRuntimeStatus, }; use geth_pipe::{PipeConnection, PipeListener, PipeMessage}; use geth_pubsub::PubsubMessage; use geth_resource::ResourceDescriptor; use geth_secrets::{BearerAccess, BearerChallenge, BearerProof, ResourceMasterSecret}; use geth_ssh_identity::{ SshCertApproval, SshCertRequest, SshCertificateRecord, SshDistributionLogEntry, SshRevocationEntry, }; use geth_ssh_proxy::SshProxyConnection; use geth_types::BlobHash; use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum ControlRequest { Status, NodeId, PeerCardExport { out: Option, }, PeerCardImport { path: PathBuf, }, PeerCardList, PeerPing { node: String, }, PeerAuthCheck { node: String, resource: String, capability: String, }, ResourceList, ResourceCreate { kind: String, name: String, }, OverlayStatus, OverlayPlan { name: String, cidr: Option, }, OverlayJoin { name: String, secret: String, cidr: Option, }, OverlayLeave { name: String, }, OverlayInterfacePlan { name: String, platform: Option, }, OverlayUp { name: String, bearer_secret: Option, mtu: Option, }, OverlayDown { name: String, }, OverlayPeers { name: String, }, OverlaySend { name: String, node: String, packet_base64: String, bearer_secret: Option, }, OverlayRecv { name: String, peek: bool, }, CasAdd { path: PathBuf, }, CasAddPrivate { resource: String, path: PathBuf, }, CasGet { hash: BlobHash, out: PathBuf, }, CasGetPrivate { resource: String, hash: BlobHash, out: PathBuf, }, CasFetch { node: String, hash: BlobHash, bearer_secret: Option, }, CasHash { path: PathBuf, }, CasHas { hash: BlobHash, }, CasPin { hash: BlobHash, }, CasUnpin { hash: BlobHash, }, CasCleanup { dry_run: bool, }, CasProviders { hash: BlobHash, }, CasList, CasRootAdd { name: String, path: PathBuf, }, CasRootList, CasRootScan { name: String, }, CasRootSync { node: String, name: String, bearer_secret: Option, }, CasRootApply { source: String, target: PathBuf, dry_run: bool, }, CasConflictRecord { root: String, path: String, kind: String, detail: String, base_tree: Option, local_tree: Option, remote_tree: Option, }, CasConflictList { root: Option, }, CasConflictResolve { conflict_id: String, resolution: String, note: Option, }, NodeList, NodeRename { node: String, name: String, signing_key_path: Option, }, NodeRevoke { node: String, signing_key_path: Option, }, NodeGrant { node: String, resource: String, capability: String, grant_id: Option, signing_key_path: Option, admin_key_path: Option, }, NodeRevokeGrant { resource: String, grant_id: String, signing_key_path: Option, admin_key_path: Option, }, NodeEndpointAdd { node: String, endpoint: String, signing_key_path: Option, }, NodeEndpointRevoke { node: String, endpoint: String, signing_key_path: Option, }, NodeEnrollRequest { node_name: String, capabilities: Vec, reason: Option, out: Option, }, NodeEnrollSubmit { owner_node: String, request_id: Option, path: Option, }, NodeEnrollImport { path: PathBuf, }, NodeEnrollList { status: Option, }, NodeEnrollApprove { request_id: String, signing_key_path: PathBuf, admin_key_path: Option, node_name: Option, capabilities: Vec, }, NodeEnrollSync { owner_node: String, }, KeychainInit { admin_key_path: Option, signing_key_path: Option, }, KeychainStatus, KeychainAdminAdd { admin_key_path: PathBuf, signing_key_path: PathBuf, principal: Option, valid_after_ms: Option, valid_before_ms: Option, }, KeychainAdminRevoke { key: String, signing_key_path: PathBuf, admin_key_path: Option, }, KeychainAllowedSigners { out: Option, }, KeychainSignFile { input: PathBuf, out: Option, namespace: Option, signing_key_path: Option, admin_key_path: Option, }, KeychainVerifyFile { input: PathBuf, signature: PathBuf, namespace: Option, allowed_signers_path: Option, principal: Option, }, KeychainSigchainExport { out: Option, }, KeychainPublishBundle { out: PathBuf, base_url: Option, signing_key_path: PathBuf, admin_key_path: Option, snapshots: Vec, }, KeychainVerifySigchain { input: PathBuf, }, KeychainImportSigchain { input: PathBuf, }, KeychainVerifyCheckpoint { checkpoint: PathBuf, signature: PathBuf, sigchain: PathBuf, allowed_signers: PathBuf, base_url: Option, principal: Option, }, KeychainFetch { url: String, out: Option, import: bool, }, KeychainExplain { op_id: String, }, KeychainExplainSigner { key: String, }, KeychainVerify, KeychainSync { node: String, }, AuthSync { node: String, }, SyncStatus, SyncNow { node: Option, }, SecretStatus, SecretCreate { resource: String, }, SecretRotate { resource: String, }, SecretBearerCreate { resource: String, capabilities: Vec, expires_at_ms: Option, }, SecretBearerList, SecretBearerChallenge { resource: String, capabilities: Vec, }, SecretBearerProve { secret: String, resource: String, capabilities: Vec, nonce: String, }, SecretBearerVerify { secret: String, resource: String, capabilities: Vec, nonce: String, response: String, }, SecretBearerRevoke { resource: String, secret: String, }, AuthExplain { subject: String, resource: String, capability: String, }, AuthGrant { subject: String, resource: String, capability: String, grant_id: Option, signing_key_path: Option, admin_key_path: Option, }, AuthRevoke { resource: String, grant_id: String, signing_key_path: Option, admin_key_path: Option, }, SshCertRequest { public_key_path: PathBuf, cert_kind: String, principals: Vec, requested_validity: Option, renewal_of: Option, reason: Option, subject: Option, }, SshCertRequests { subject: Option, }, SshCertApprove { request_id: String, ca_key_path: PathBuf, valid_for: Option, serial: Option, out: Option, sign: bool, subject: Option, }, SshCertImport { request_id: String, cert_path: PathBuf, subject: Option, }, SshCertList { subject: Option, }, SshCertSync { node: String, bearer_secret: Option, }, SshRevocationAdd { kind: String, target: String, reason: Option, subject: Option, }, SshRevocationList { subject: Option, }, SshRevocationExport { out: PathBuf, format: String, ca_public: Option, subject: Option, }, SshRevocationImport { path: PathBuf, format: String, subject: Option, }, SshRevocationSync { node: String, bearer_secret: Option, }, SshProxyConnect { node: String, bearer_secret: Option, }, SshProxyStream { node: String, bearer_secret: Option, }, SshAdminShell { node: String, command: String, bearer_secret: Option, }, DbAdd { name: String, path: PathBuf, }, DbStatus { name: String, }, DbChanges { name: String, after_db_version: Option, limit: u32, }, DbSync { node: String, name: String, limit: u32, bearer_secret: Option, }, KvCreate { name: String, }, KvSet { name: String, key: String, value: String, subject: Option, }, KvGet { name: String, key: String, }, KvSync { node: String, name: String, bearer_secret: Option, }, DocumentCreate { name: String, }, DocumentStatus { name: String, }, DocumentSet { name: String, state_json: String, }, DocumentGet { name: String, }, DocumentSync { node: String, name: String, bearer_secret: Option, }, PubsubPub { topic: String, message: String, node: Option, bearer_secret: Option, }, PubsubSub { topic: String, node: Option, bearer_secret: Option, }, PipeListen { name: String, node: Option, bearer_secret: Option, }, PipeConnect { target: String, node: Option, bearer_secret: Option, }, PipeTcpForward { node: String, listen_addr: String, target_addr: String, bearer_secret: Option, }, PipeTcpStream { node: String, target_addr: String, bearer_secret: Option, }, PipeUnixForward { node: String, listen_path: PathBuf, target_path: PathBuf, bearer_secret: Option, }, PipeUnixStream { node: String, target_path: PathBuf, bearer_secret: Option, }, PipeSend { target: String, data_base64: String, node: Option, bearer_secret: Option, }, PipeRecv { name: String, peek: bool, }, ModuleStub { module: String, command: String, }, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum ControlResponse { Status(StatusResponse), NodeId(NodeIdResponse), PeerCardExported { card: PeerCard, out: Option, note: String, }, PeerCardImported { peer: DiscoveredPeer, note: String, }, PeerCardList { peers: Vec, note: String, }, PeerPinged { peer_node_id: String, peer_agent_id: String, endpoint_id: String, alpn: String, note: String, }, PeerAuthChecked { peer_node_id: String, peer_agent_id: String, endpoint_id: String, resource: String, capability: String, allowed: bool, reason: String, evaluated_ops: usize, note: String, }, ResourceList { resources: Vec, }, ResourceCreated { resource: ResourceDescriptor, }, OverlayStatus { networks: Vec, note: String, }, OverlayPlanned { plan: OverlayPlan, }, OverlayJoined { join: OverlayJoinPlan, }, OverlayLeft { name: String, stopped: bool, note: String, }, OverlayInterfacePlanned { plan: OverlayInterfacePlan, }, OverlayRuntimeStarted { status: OverlayRuntimeStatus, }, OverlayRuntimeStopped { name: String, stopped: bool, note: String, }, OverlayPeers { name: String, peers: Vec, note: String, }, OverlayPacketSent { peer_node_id: String, peer_agent_id: String, endpoint_id: String, packet: Option, allowed: bool, reason: String, note: String, }, OverlayPackets { name: String, packets: Vec, drained: bool, note: String, }, CasAdded { hash: BlobHash, size_bytes: u64, }, CasPrivateAdded { resource: String, epoch: u64, plaintext_hash: BlobHash, encrypted_hash: BlobHash, size_bytes: u64, note: String, }, CasGot { hash: BlobHash, out: PathBuf, size_bytes: u64, }, CasPrivateGot { resource: String, hash: BlobHash, plaintext_hash: BlobHash, out: PathBuf, size_bytes: u64, note: String, }, CasFetched { peer_node_id: String, peer_agent_id: String, endpoint_id: String, hash: BlobHash, size_bytes: u64, allowed: bool, reason: String, note: String, }, CasHash { hash: BlobHash, }, CasHas { hash: BlobHash, present: bool, }, CasPinned { hash: BlobHash, pinned: bool, }, CasCleanup { removed: Vec, retained_pinned: Vec, dry_run: bool, }, CasList { blobs: Vec, }, CasProviders { hash: BlobHash, providers: Vec, }, CasRootAdded { root: FileRoot, }, CasRootList { roots: Vec, }, CasRootScanned { scan: FileRootScan, }, CasRootSynced { peer_node_id: String, peer_agent_id: String, endpoint_id: String, name: String, root: Option, tree_bytes_imported: bool, sync_conflicts: Vec, allowed: bool, reason: String, note: String, }, CasRootApplied { source: String, target: PathBuf, files_written: usize, dirs_created: usize, conflicts: Vec, dry_run: bool, note: String, }, CasConflictRecorded { conflict: FileConflict, }, CasConflictList { conflicts: Vec, }, CasConflictResolved { conflict: FileConflict, }, KeychainStatus(KeychainStatusResponse), KeychainInitialized { ops: Vec, signatures: Vec, }, KeychainAdminUpdated { op: KeychainOp, signatures: Vec, note: String, }, KeychainAllowedSigners { entries: Vec, allowed_signers: String, out: Option, note: String, }, KeychainFileSigned { input: PathBuf, out: Option, namespace: String, signer: String, note: String, }, KeychainFileVerified { input: PathBuf, signature: PathBuf, namespace: String, verified: bool, principal: Option, note: String, }, KeychainSigchainExported { entries: Vec, jsonl: String, out: Option, note: String, }, KeychainBundlePublished { out: PathBuf, base_url: String, allowed_signers_path: PathBuf, sigchain_path: PathBuf, checkpoint_path: PathBuf, checkpoint_signature_path: PathBuf, checkpoint: KeychainCheckpoint, snapshots: Vec, note: String, }, KeychainSigchainFileVerified { input: PathBuf, report: KeychainSigchainReport, note: String, }, KeychainSigchainImported { input: PathBuf, ops_imported: usize, signatures_imported: usize, invalid_ops_rejected: usize, note: String, }, KeychainCheckpointVerified { checkpoint: KeychainCheckpoint, verified: bool, principal: Option, note: String, }, KeychainFetched { url: String, out: PathBuf, checkpoint: KeychainCheckpoint, imported: Option, note: String, }, KeychainExplained { subject: String, lines: Vec, }, KeychainVerified { report: KeychainSigchainReport, }, KeychainSynced { peer_node_id: String, peer_agent_id: String, endpoint_id: String, ops_imported: usize, signatures_imported: usize, invalid_ops_rejected: usize, high_water_ms: i64, note: String, }, AuthSynced { peer_node_id: String, peer_agent_id: String, endpoint_id: String, ops_imported: usize, signatures_imported: usize, invalid_ops_rejected: usize, high_water_ms: i64, note: String, }, SyncStatus { peers: Vec, note: String, }, SyncRan { peers: Vec, note: String, }, SecretStatus { secrets: Vec, }, SecretCreated { secret: ResourceMasterSecret, }, SecretBearerCreated { access: BearerAccess, }, SecretBearerList { access: Vec, }, SecretBearerChallenge { challenge: BearerChallenge, }, SecretBearerProof { proof: BearerProof, }, SecretBearerVerified { secret: String, resource: String, capabilities: Vec, verified: bool, reason: String, }, SecretBearerRevoked { resource: String, secret: String, }, AuthExplain(AuthExplanation), AuthOpRecorded { op: AuthOp, signatures: Vec, }, NodeList { nodes: Vec, note: String, }, NodeKeychainUpdated { ops: Vec, signatures: Vec, note: String, }, NodeGrantUpdated { op: AuthOp, signatures: Vec, note: String, }, NodeEnrollmentRequested { request: NodeEnrollmentRequest, out: Option, note: String, }, NodeEnrollmentSubmitted { request_id: String, owner_node_id: String, accepted: bool, note: String, }, NodeEnrollmentImported { request: NodeEnrollmentRequest, note: String, }, NodeEnrollmentList { requests: Vec, note: String, }, NodeEnrollmentApproved { request: NodeEnrollmentRequest, keychain_ops: Vec, keychain_signatures: Vec, auth_ops: Vec, auth_signatures: Vec, note: String, }, NodeEnrollmentSynced { owner_node: String, keychain_ops_imported: usize, keychain_signatures_imported: usize, auth_ops_imported: usize, auth_signatures_imported: usize, invalid_ops_rejected: usize, note: String, }, SshCertRequested { request: SshCertRequest, }, SshCertRequests { requests: Vec, }, SshCertApproved { approval: SshCertApproval, }, SshCertImported { certificate: SshCertificateRecord, }, SshCertList { requests: Vec, certificates: Vec, }, SshCertSynced { peer_node_id: String, peer_agent_id: String, endpoint_id: String, requests_imported: usize, certificates_imported: usize, allowed: bool, reason: String, note: String, }, SshRevocationAdded { revocation: SshRevocationEntry, }, SshRevocationList { revocations: Vec, }, SshRevocationExported { out: PathBuf, format: String, count: usize, note: String, }, SshRevocationImported { revocations: Vec, format: String, count: usize, note: String, }, SshRevocationSynced { peer_node_id: String, peer_agent_id: String, endpoint_id: String, revocations_imported: usize, allowed: bool, reason: String, note: String, }, DbAdded { db: DbResource, }, DbStatus { db: DbResource, }, DbChanges { db: DbResource, batch: CrSqliteChangeBatch, }, DbSynced { peer_node_id: String, peer_agent_id: String, endpoint_id: String, name: String, changes_received: usize, changes_applied: usize, max_db_version: Option, schema_match: bool, allowed: bool, reason: String, note: String, }, KvCreated { kv: KvResource, }, KvSet { entry: KvEntry, }, KvGet { entry: Option, }, KvSynced { peer_node_id: String, peer_agent_id: String, endpoint_id: String, name: String, entries_imported: usize, allowed: bool, reason: String, note: String, }, DocumentCreated { document: DocumentResource, }, DocumentStatus { document: DocumentResource, }, DocumentSet { state: DocumentState, }, DocumentGet { state: DocumentState, }, DocumentSynced { peer_node_id: String, peer_agent_id: String, endpoint_id: String, name: String, updated: bool, allowed: bool, reason: String, note: String, }, PubsubPublished { message: PubsubMessage, }, PubsubRemotePublished { peer_node_id: String, peer_agent_id: String, endpoint_id: String, message: PubsubMessage, allowed: bool, reason: String, note: String, }, PubsubMessages { topic: String, messages: Vec, note: String, }, PubsubRemoteMessages { peer_node_id: String, peer_agent_id: String, endpoint_id: String, topic: String, messages: Vec, allowed: bool, reason: String, note: String, }, PipeListening { listener: PipeListener, }, PipeRemoteListening { peer_node_id: String, peer_agent_id: String, endpoint_id: String, listener: Option, allowed: bool, reason: String, note: String, }, PipeConnected { connection: PipeConnection, }, PipeRemoteConnected { peer_node_id: String, peer_agent_id: String, endpoint_id: String, connection: PipeConnection, allowed: bool, reason: String, note: String, }, PipeSent { message: Option, listener_found: bool, note: String, }, PipeRemoteSent { peer_node_id: String, peer_agent_id: String, endpoint_id: String, message: Option, listener_found: bool, allowed: bool, reason: String, note: String, }, PipeMessages { name: String, messages: Vec, drained: bool, note: String, }, SshProxyConnected { peer_node_id: String, peer_agent_id: String, endpoint_id: String, connection: Option, allowed: bool, reason: String, note: String, }, SshAdminShellOutput { peer_node_id: String, peer_agent_id: String, endpoint_id: String, command: String, output: String, allowed: bool, reason: String, note: String, }, NotImplemented { module: String, command: String, }, Error { message: String, }, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct KeychainFetchImportReport { pub ops_imported: usize, pub signatures_imported: usize, pub invalid_ops_rejected: usize, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct KeychainPublishedSnapshot { pub name: String, pub source: PathBuf, pub path: PathBuf, pub signature_path: PathBuf, pub namespace: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct StatusResponse { pub home: PathBuf, pub socket: PathBuf, pub agent_id: String, pub node_id: String, pub daemon_started_at_ms: i64, pub daemon_uptime_seconds: u64, pub store_schema_version: i64, pub store_current_schema_version: i64, pub store_journal_mode: String, pub store_synchronous: String, pub store_status: String, pub store_note: String, pub iroh_enabled: bool, pub endpoint_id: Option, pub iroh_relay_mode: String, pub iroh_local_discovery: bool, pub iroh: String, #[serde(default)] pub native_backends: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct NativeBackendStatus { pub module: String, pub current_backend: String, pub target_crate: String, pub target_version: String, pub status: String, pub blocker: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct NodeIdResponse { pub agent_id: String, pub node_id: String, pub endpoint_id: Option, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct KeychainStatusResponse { pub initialized: bool, pub admin_keys: usize, pub signatures: usize, pub verified_signatures: usize, pub failed_signatures: usize, pub users: usize, pub devices: usize, pub nodes: usize, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CasBlob { pub hash: BlobHash, pub size_bytes: u64, pub pinned: bool, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CasProvider { pub peer_node_id: String, pub endpoint_id: String, pub last_seen_ms: i64, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct OverlayPeer { pub node_id: String, pub endpoint_id: Option, pub virtual_ip: Option, pub state: String, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum PeerControlRequest { Ping { peer_card: PeerCard, nonce: String, }, AuthCheck { peer_card: PeerCard, resource: String, capability: String, nonce: String, }, SyncStatus { peer_card: PeerCard, nonce: String, }, KeychainSync { peer_card: PeerCard, since_ms: i64, nonce: String, }, AuthSync { peer_card: PeerCard, since_ms: i64, nonce: String, }, NodeEnrollmentSubmit { peer_card: PeerCard, request: NodeEnrollmentRequest, nonce: String, }, CasFetch { peer_card: PeerCard, hash: BlobHash, nonce: String, bearer_proof: Option, }, CasRootSync { peer_card: PeerCard, name: String, nonce: String, bearer_proof: Option, }, SshCertSync { peer_card: PeerCard, since_ms: i64, nonce: String, bearer_proof: Option, }, SshRevocationSync { peer_card: PeerCard, since_ms: i64, nonce: String, bearer_proof: Option, }, KvSync { peer_card: PeerCard, name: String, since_ms: i64, nonce: String, bearer_proof: Option, }, PubsubPublish { peer_card: PeerCard, topic: String, message: String, nonce: String, bearer_proof: Option, }, PubsubSubscribe { peer_card: PeerCard, topic: String, nonce: String, bearer_proof: Option, }, PipeConnect { peer_card: PeerCard, target: String, nonce: String, bearer_proof: Option, }, PipeListen { peer_card: PeerCard, name: String, nonce: String, bearer_proof: Option, }, SshProxyConnect { peer_card: PeerCard, nonce: String, bearer_proof: Option, }, SshAdminShell { peer_card: PeerCard, command: String, nonce: String, bearer_proof: Option, }, DocumentSync { peer_card: PeerCard, name: String, since_ms: i64, nonce: String, bearer_proof: Option, }, DbSync { peer_card: PeerCard, name: String, after_db_version: Option, limit: u32, nonce: String, bearer_proof: Option, }, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum PeerControlResponse { Pong { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, alpn: String, nonce: String, note: String, }, AuthChecked { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, resource: String, capability: String, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, SyncStatus { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, watermarks: Vec, nonce: String, note: String, }, KeychainSynced { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, ops: Vec, signatures: Vec, high_water_ms: i64, nonce: String, note: String, }, AuthSynced { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, ops: Vec, signatures: Vec, high_water_ms: i64, nonce: String, note: String, }, NodeEnrollmentSubmitted { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, request_id: String, accepted: bool, nonce: String, note: String, }, CasFetched { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, hash: BlobHash, size_bytes: u64, content_base64: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, CasRootSynced { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, name: String, root: Option, tree_content_base64: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, SshCertSynced { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, requests: Vec, certificates: Vec, log_entries: Vec, high_water_ms: i64, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, SshRevocationSynced { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, revocations: Vec, log_entries: Vec, high_water_ms: i64, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, KvSynced { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, name: String, entries: Vec, high_water_ms: i64, docs_ticket: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, PubsubPublished { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, message: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, PubsubSubscribed { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, topic: String, messages: Vec, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, PipeConnected { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, connection: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, PipeListening { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, listener: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, SshProxyConnected { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, connection: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, SshAdminShellOutput { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, command: String, output: String, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, DocumentSynced { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, name: String, state: Option, high_water_ms: i64, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, DbSynced { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, name: String, batch: Option, high_water_db_version: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, Error { message: String, }, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum PipeWireRequest { Send { peer_card: PeerCard, target: String, data_base64: String, nonce: String, bearer_proof: Option, }, TcpConnect { peer_card: PeerCard, target_addr: String, nonce: String, bearer_proof: Option, }, UnixConnect { peer_card: PeerCard, target_path: PathBuf, nonce: String, bearer_proof: Option, }, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum PipeWireResponse { Sent { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, message: Box>, listener_found: bool, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, Connected { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, connection: Option, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, Error { message: String, }, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum OverlayWireRequest { Packet { peer_card: PeerCard, network: String, packet_base64: String, nonce: String, bearer_proof: Option, }, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "kebab-case")] pub enum OverlayWireResponse { PacketAccepted { node_id: String, agent_id: String, endpoint_id: String, remote_endpoint_id: String, packet: Option>, allowed: bool, reason: String, evaluated_ops: usize, nonce: String, note: String, }, Error { message: String, }, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SyncWatermark { pub stream: String, pub high_water: i64, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SyncPeerStatus { pub peer_node_id: String, pub streams: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SyncStreamStatus { pub stream: String, pub cursor_ms: i64, pub state: String, pub stale: bool, pub stale_after_ms: i64, pub last_attempt_ms: Option, pub last_success_ms: Option, pub last_error: Option, pub last_imported: usize, pub last_rejected: usize, pub next_action: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SyncPeerRun { pub peer_node_id: String, pub streams: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SyncStreamRun { pub stream: String, pub attempted: bool, pub success: bool, pub imported: usize, pub rejected: usize, pub cursor_ms: i64, pub error: Option, } #[derive(Debug, thiserror::Error)] pub enum ControlError { #[error("json error: {0}")] Json(#[from] serde_json::Error), } pub fn encode_request(request: &ControlRequest) -> Result { let mut line = serde_json::to_string(request)?; line.push('\n'); Ok(line) } pub fn decode_request(line: &str) -> Result { serde_json::from_str(line).map_err(ControlError::from) } pub fn encode_response(response: &ControlResponse) -> Result { let mut line = serde_json::to_string(response)?; line.push('\n'); Ok(line) } pub fn decode_response(line: &str) -> Result { serde_json::from_str(line).map_err(ControlError::from) } pub fn encode_peer_request(request: &PeerControlRequest) -> Result { let mut line = serde_json::to_string(request)?; line.push('\n'); Ok(line) } pub fn decode_peer_request(line: &str) -> Result { serde_json::from_str(line).map_err(ControlError::from) } pub fn encode_peer_response(response: &PeerControlResponse) -> Result { let mut line = serde_json::to_string(response)?; line.push('\n'); Ok(line) } pub fn decode_peer_response(line: &str) -> Result { serde_json::from_str(line).map_err(ControlError::from) } pub fn encode_pipe_wire_request(request: &PipeWireRequest) -> Result { let mut line = serde_json::to_string(request)?; line.push('\n'); Ok(line) } pub fn decode_pipe_wire_request(line: &str) -> Result { serde_json::from_str(line).map_err(ControlError::from) } pub fn encode_pipe_wire_response(response: &PipeWireResponse) -> Result { let mut line = serde_json::to_string(response)?; line.push('\n'); Ok(line) } pub fn decode_pipe_wire_response(line: &str) -> Result { serde_json::from_str(line).map_err(ControlError::from) } pub fn encode_overlay_wire_request(request: &OverlayWireRequest) -> Result { let mut line = serde_json::to_string(request)?; line.push('\n'); Ok(line) } pub fn decode_overlay_wire_request(line: &str) -> Result { serde_json::from_str(line).map_err(ControlError::from) } pub fn encode_overlay_wire_response( response: &OverlayWireResponse, ) -> Result { let mut line = serde_json::to_string(response)?; line.push('\n'); Ok(line) } pub fn decode_overlay_wire_response(line: &str) -> Result { serde_json::from_str(line).map_err(ControlError::from) } #[cfg(test)] mod tests { use super::*; use serde_json::{Map, Value, json}; const CONTROL_REQUEST_VARIANTS: usize = 122; const CONTROL_RESPONSE_VARIANTS: usize = 114; const PEER_CONTROL_REQUEST_VARIANTS: usize = 19; const PEER_CONTROL_RESPONSE_VARIANTS: usize = 20; const PIPE_WIRE_REQUEST_VARIANTS: usize = 3; const PIPE_WIRE_RESPONSE_VARIANTS: usize = 3; const OVERLAY_WIRE_REQUEST_VARIANTS: usize = 1; const OVERLAY_WIRE_RESPONSE_VARIANTS: usize = 2; #[test] fn all_protocol_variants_have_roundtrip_samples() { roundtrip_declared_variants::( "ControlRequest", CONTROL_REQUEST_VARIANTS, decode_request, encode_request, ); roundtrip_declared_variants::( "ControlResponse", CONTROL_RESPONSE_VARIANTS, decode_response, encode_response, ); roundtrip_declared_variants::( "PeerControlRequest", PEER_CONTROL_REQUEST_VARIANTS, decode_peer_request, encode_peer_request, ); roundtrip_declared_variants::( "PeerControlResponse", PEER_CONTROL_RESPONSE_VARIANTS, decode_peer_response, encode_peer_response, ); roundtrip_declared_variants::( "PipeWireRequest", PIPE_WIRE_REQUEST_VARIANTS, decode_pipe_wire_request, encode_pipe_wire_request, ); roundtrip_declared_variants::( "PipeWireResponse", PIPE_WIRE_RESPONSE_VARIANTS, decode_pipe_wire_response, encode_pipe_wire_response, ); roundtrip_declared_variants::( "OverlayWireRequest", OVERLAY_WIRE_REQUEST_VARIANTS, decode_overlay_wire_request, encode_overlay_wire_request, ); roundtrip_declared_variants::( "OverlayWireResponse", OVERLAY_WIRE_RESPONSE_VARIANTS, decode_overlay_wire_response, encode_overlay_wire_response, ); } fn roundtrip_declared_variants( enum_name: &str, expected_count: usize, decode: fn(&str) -> Result, encode: fn(&T) -> Result, ) where T: std::fmt::Debug + PartialEq, { let variants = declared_enum_variants(enum_name); assert_eq!(variants.len(), expected_count, "{enum_name} variant count"); for variant in variants { let sample = protocol_sample(&variant); let encoded_sample = serde_json::to_string(&sample).expect("sample json"); let decoded = decode(&encoded_sample).unwrap_or_else(|err| { panic!("{enum_name}::{} sample decodes: {err}", variant.name) }); let encoded = encode(&decoded).unwrap_or_else(|err| { panic!("{enum_name}::{} sample encodes: {err}", variant.name) }); let decoded_again = decode(&encoded).unwrap_or_else(|err| { panic!( "{enum_name}::{} encoded sample decodes: {err}", variant.name ) }); assert_eq!( decoded_again, decoded, "{enum_name}::{} roundtrips through line codec", variant.name ); } } #[derive(Debug)] struct EnumVariant { name: String, body: VariantBody, } #[derive(Debug)] enum VariantBody { Unit, Tuple(String), Struct(Vec<(String, String)>), } fn declared_enum_variants(enum_name: &str) -> Vec { let source = include_str!("lib.rs"); let needle = format!("pub enum {enum_name}"); let start = source.find(&needle).expect("enum exists"); let after_name = &source[start + needle.len()..]; let open = after_name.find('{').expect("enum opens"); let block = enum_block(&after_name[open..]); split_top_level(block) .into_iter() .filter_map(|decl| parse_variant_decl(decl.trim())) .collect() } fn enum_block(input: &str) -> &str { let mut depth = 0_i32; let mut start = None; for (idx, ch) in input.char_indices() { match ch { '{' => { depth += 1; if start.is_none() { start = Some(idx + 1); } } '}' => { depth -= 1; if depth == 0 { return &input[start.expect("block start")..idx]; } } _ => {} } } panic!("unterminated enum block"); } fn split_top_level(input: &str) -> Vec<&str> { let mut parts = Vec::new(); let mut start = 0; let mut brace_depth = 0_i32; let mut paren_depth = 0_i32; let mut angle_depth = 0_i32; for (idx, ch) in input.char_indices() { match ch { '{' => brace_depth += 1, '}' => brace_depth -= 1, '(' => paren_depth += 1, ')' => paren_depth -= 1, '<' => angle_depth += 1, '>' => angle_depth -= 1, ',' if brace_depth == 0 && paren_depth == 0 && angle_depth == 0 => { parts.push(&input[start..idx]); start = idx + 1; } _ => {} } } if start < input.len() { parts.push(&input[start..]); } parts } fn parse_variant_decl(decl: &str) -> Option { let decl = decl.trim(); if decl.is_empty() || decl.starts_with("#[") { return None; } let name_len = decl .find(|ch: char| !(ch.is_ascii_alphanumeric() || ch == '_')) .unwrap_or(decl.len()); let name = decl[..name_len].to_owned(); let rest = decl[name_len..].trim(); let body = if rest.starts_with('{') { let fields = split_top_level(enum_block(rest)) .into_iter() .filter_map(|field| { let (name, ty) = field.trim().split_once(':')?; Some((name.trim().to_owned(), ty.trim().to_owned())) }) .collect(); VariantBody::Struct(fields) } else if let Some(tuple) = rest .strip_prefix('(') .and_then(|rest| rest.split(')').next()) { VariantBody::Tuple(tuple.trim().to_owned()) } else { VariantBody::Unit }; Some(EnumVariant { name, body }) } fn protocol_sample(variant: &EnumVariant) -> Value { let mut object = Map::new(); object.insert("type".to_owned(), Value::String(kebab_case(&variant.name))); match &variant.body { VariantBody::Unit => {} VariantBody::Tuple(ty) => { let Value::Object(fields) = sample_for_type(ty) else { panic!("tuple variant {} must use object sample", variant.name); }; object.extend(fields); } VariantBody::Struct(fields) => { for (name, ty) in fields { object.insert(name.clone(), sample_for_type(ty)); } } } Value::Object(object) } fn kebab_case(name: &str) -> String { let mut out = String::new(); for (idx, ch) in name.chars().enumerate() { if ch.is_ascii_uppercase() { if idx > 0 { out.push('-'); } out.push(ch.to_ascii_lowercase()); } else { out.push(ch); } } out } fn sample_for_type(ty: &str) -> Value { let ty = compact_type(ty); if let Some(inner) = ty .strip_prefix("Option<") .and_then(|ty| ty.strip_suffix('>')) { let _ = inner; return Value::Null; } if ty.starts_with("Vec<") { return json!([]); } if let Some(inner) = ty.strip_prefix("Box<").and_then(|ty| ty.strip_suffix('>')) { return sample_for_type(inner); } match ty.as_str() { "String" | "PathBuf" => json!("sample"), "bool" => json!(true), "usize" | "u64" | "u32" | "u16" | "i64" => json!(1), "BlobHash" => json!("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), "StatusResponse" => json!({ "home": "/tmp/geth-home", "socket": "/tmp/geth.sock", "agent_id": "agent:local", "node_id": "node:local", "daemon_started_at_ms": 1, "daemon_uptime_seconds": 1, "store_schema_version": 1, "store_current_schema_version": 1, "store_journal_mode": "wal", "store_synchronous": "normal", "store_status": "ok", "store_note": "sample", "iroh_enabled": true, "endpoint_id": "endpoint:local", "iroh_relay_mode": "default", "iroh_local_discovery": true, "iroh": "enabled", "native_backends": [] }), "NodeIdResponse" => json!({ "agent_id": "agent:local", "node_id": "node:local", "endpoint_id": "endpoint:local" }), "KeychainStatusResponse" => json!({ "initialized": true, "admin_keys": 1, "signatures": 1, "verified_signatures": 1, "failed_signatures": 0, "users": 1, "devices": 1, "nodes": 1 }), "KeychainFetchImportReport" => json!({ "ops_imported": 1, "signatures_imported": 1, "invalid_ops_rejected": 0 }), "ResourceDescriptor" => json!({ "id": "resource:sample", "kind": "kv", "name": "sample", "authority": {"kind": "local"}, "local_role": "owner", "replication": "local-only", "retention": "keep", "status": "active" }), "PeerCard" => peer_card_json(), "DiscoveredPeer" => json!({ "card": peer_card_json(), "discovered_at": 1, "source": "manual", "trust_state": "candidate-only" }), "OverlayPlan" => json!({ "name": "home", "resource": "resource:overlay:home", "cidr": "172.22.0.0/24", "alpn": "/geth/overlay/1", "capabilities": [], "discovery": "manual", "runtime": "planned", "security": [], "implementation_notes": [] }), "OverlayJoinPlan" => json!({ "plan": sample_for_type("OverlayPlan"), "network": sample_for_type("OverlayNetworkStatus"), "enabled": true, "note": "sample" }), "OverlayNetworkStatus" => json!({ "name": "home", "resource": "resource:overlay:home", "cidr": "172.22.0.0/24", "state": "joined", "virtual_ip": "172.22.0.2", "peers": [], "note": "sample" }), "OverlayInterfacePlan" => json!({ "name": "home", "platform": "linux", "interface_name": "geth-home", "cidr": "172.22.0.0/24", "virtual_ip": "172.22.0.2", "requires_privileges": true, "commands": [], "notes": [] }), "OverlayRuntimeStatus" => json!({ "name": "home", "interface_name": "geth-home", "virtual_ip": "172.22.0.2", "cidr": "172.22.0.0/24", "mtu": 1280, "started_at_ms": 1, "packets_from_tun": 0, "packets_to_tun": 0, "packets_to_peers": 0, "last_error": null, "note": "sample" }), "OverlayPacket" => json!({ "id": "overlay-packet:sample", "network": "home", "source_node": "node:local", "destination_node": "node:peer", "packet_base64": "AA==", "size_bytes": 1, "received_at_ms": 1, "note": "sample" }), "FileRoot" => json!({ "id": "file-root:sample", "resource": "resource:cas-root:sample", "name": "sample", "path": "/tmp/sample", "latest_tree": null, "updated_at_ms": 1 }), "FileRootScan" => json!({ "root": sample_for_type("FileRoot"), "tree": { "hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "size_bytes": 1 }, "changes": [], "note": "sample" }), "FileConflict" => json!({ "id": "file-conflict:sample", "root": "sample", "resource": "resource:cas-root:sample", "path": "file.txt", "kind": "concurrent-edit", "status": "open", "base_tree": null, "local_tree": null, "remote_tree": null, "detail": "sample", "resolution": null, "resolution_note": null, "created_at_ms": 1, "resolved_at_ms": null }), "KeychainOp" => json!({ "id": "auth-op:sample", "created_at": 1, "kind": {"kind": "keychain-init"} }), "KeychainOpSignature" => json!({ "op_id": "auth-op:sample", "signer": "key:sample", "signer_public_key": "public", "namespace": "geth.keychain.v1@geth.local", "signature": [1], "created_at": 1 }), "KeychainAllowedSigner" => json!({ "key": "key:sample", "principal": "admin", "public_key": "public", "valid_after_ms": null, "valid_before_ms": null }), "KeychainSigchainReport" => json!({ "ops": 1, "signatures": 1, "accepted_ops": 1, "rejected_ops": 0, "active_admin_keys": 1, "accepted_head": null, "note": "sample" }), "KeychainSigchainEntry" => json!({ "op": sample_for_type("KeychainOp"), "signatures": [] }), "KeychainCheckpoint" => json!({ "version": 1, "profile": { "keychain_signature_namespace": "geth.keychain.v1@geth.local", "node_enrollment_request_namespace": "geth.node-enrollment-request.v1@geth.local", "default_admin_principal": "geth-admin" }, "base_url": "https://example.invalid", "head": null, "ops": 1, "signatures": 1, "sigchain_bytes": 1, "sigchain_hash": "hash", "allowed_signers_hash": "hash", "reduced_view_hash": "hash", "generated_at": 1 }), "AuthExplanation" => json!({ "subject": "node:peer", "resource": "resource:sample", "capability": "sample.read", "allowed": true, "reason": "sample", "evaluated_ops": 1, "diagnostics": [] }), "AuthOp" => json!({ "id": "auth-op:sample", "resource": "resource:sample", "created_at": 1, "kind": {"kind": "resource-create"} }), "AuthOpSignature" => json!({ "op_id": "auth-op:sample", "signer": "key:sample", "signer_public_key": "public", "namespace": "geth.auth.v1@geth.local", "signature": [1], "created_at": 1 }), "NodeRecord" => json!({ "id": "node:peer", "device": "device:peer", "name": "peer", "endpoints": [] }), "NodeEnrollmentRequest" => json!({ "id": "auth-op:enroll", "requester_node": "node:peer", "requester_agent": "agent:peer", "requester_agent_public_key": "public", "requested_node_name": "peer", "requested_capabilities": [], "endpoint_id": null, "reason": null, "status": "pending", "created_at": 1, "provenance": null }), "ResourceMasterSecret" => json!({ "id": "secret:sample", "resource": "resource:sample", "epoch": 1, "created_at": 1 }), "BearerAccess" => json!({ "secret": "bearer:sample", "token": null, "token_hash": "hash", "resource": "resource:sample", "capabilities": ["sample.read"], "expires_at": null, "may_delegate": false }), "BearerChallenge" => json!({ "resource": "resource:sample", "capabilities": ["sample.read"], "nonce": "nonce", "issued_at": 1 }), "BearerProof" => json!({ "secret": "bearer:sample", "resource": "resource:sample", "capabilities": ["sample.read"], "nonce": "nonce", "response": "response" }), "SshCertRequest" => json!({ "id": "ssh-cert-request:sample", "requester_node": "node:peer", "public_key": "ssh-ed25519 AAAA sample", "public_key_fingerprint": "SHA256:sample", "cert_kind": "user", "principals": [], "requested_validity": null, "renewal_of": null, "reason": null, "status": "pending", "created_at": 1, "provenance": null }), "SshCertApproval" => json!({ "request_id": "ssh-cert-request:sample", "approved_by_node": "node:local", "ca_key_path": "/tmp/ca", "key_id": "geth", "valid_for": "+1h", "serial": null, "output_path": null, "signing_command": [], "signed": false, "certificate_id": null, "note": "sample" }), "SshCertificateRecord" => json!({ "id": "ssh-cert:sample", "request_id": "ssh-cert-request:sample", "certificate": "cert", "certificate_fingerprint": "SHA256:sample", "imported_at": 1, "provenance": null }), "SshRevocationEntry" => json!({ "id": "ssh-revocation:sample", "kind": "public-key", "target": "ssh-ed25519 AAAA sample", "reason": null, "created_at": 1, "published": false, "provenance": null }), "DbResource" => json!({ "id": "db:sample", "resource": "resource:db:sample", "name": "sample", "path": "/tmp/sample.sqlite", "path_exists": true, "size_bytes": 1, "schema_metadata": "schema", "crsqlite_changes": { "available": false, "change_count": null, "max_db_version": null, "columns": [], "error": null }, "sync_status": "local" }), "CrSqliteChangeBatch" => json!({ "schema_metadata": "schema", "max_db_version": null, "changes": [] }), "KvResource" => json!({ "id": "kv:sample", "resource": "resource:kv:sample", "name": "sample", "sync_status": "local" }), "KvEntry" => json!({ "store": "kv:sample", "key": "key", "value": "value" }), "DocumentResource" => json!({ "id": "document:sample", "resource": "resource:document:sample", "name": "sample", "sync_status": "local", "state_bytes": 2 }), "DocumentState" => json!({ "document": sample_for_type("DocumentResource"), "state_json": "{}", "updated_at": 1 }), "PubsubMessage" => json!({ "topic": "topic:sample", "message": "sample", "published_at": 1 }), "PipeListener" => json!({ "id": "pipe:sample", "name": "sample", "listened_at": 1, "note": "sample" }), "PipeConnection" => json!({ "target": "sample", "connected_at": 1, "local_listener_found": true, "note": "sample" }), "PipeMessage" => json!({ "pipe": "sample", "data_base64": "AA==", "received_at": 1, "source_node": null, "note": "sample" }), "SshProxyConnection" => json!({ "target_node": "node:peer", "connected_at": 1, "local_sshd_target": "127.0.0.1:22", "admin_shell_available": false, "note": "sample" }), other => panic!("missing protocol sample for type {other}"), } } fn compact_type(ty: &str) -> String { ty.chars().filter(|ch| !ch.is_whitespace()).collect() } fn peer_card_json() -> Value { json!({ "node_id": "node:peer", "agent_id": "agent:peer", "endpoints": [], "issued_at": 1, "signature": { "namespace": "geth.peer-card.v1@geth.local", "signer": "agent:peer", "public_key": "public", "signature": "signature" } }) } #[test] fn control_request_response_serialization_roundtrip() { let request = ControlRequest::KeychainInit { admin_key_path: Some(PathBuf::from("admin.pub")), signing_key_path: Some(PathBuf::from("admin")), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::NodeRename { node: "laptop".to_owned(), name: "work-laptop".to_owned(), signing_key_path: Some(PathBuf::from("admin")), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::NodeEnrollRequest { node_name: "workstation".to_owned(), capabilities: vec!["resource:ssh-proxy:local=ssh_proxy.connect".to_owned()], reason: Some("new machine".to_owned()), out: Some(PathBuf::from("enrollment.json")), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::NodeEnrollSync { owner_node: "owner-laptop".to_owned(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::KeychainSync { node: "work-laptop".to_owned(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::CasHas { hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::OverlayPlan { name: "home-lan".to_owned(), cidr: Some("172.22.0.0/24".to_owned()), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::OverlayUp { name: "home-lan".to_owned(), bearer_secret: Some("gbt_route".to_owned()), mtu: Some(1280), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::OverlaySend { name: "home-lan".to_owned(), node: "node:peer".to_owned(), packet_base64: "RQAAFAAAQABAAQAAqBYAAawWAAI=".to_owned(), bearer_secret: Some("gbt_invite".to_owned()), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::CasHas { hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(), present: true, }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::OverlayPlanned { plan: geth_overlay::plan_overlay("home-lan", None, geth_overlay::OVERLAY_ALPN) .expect("overlay plan"), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::OverlayJoined { join: geth_overlay::OverlayJoinPlan { plan: geth_overlay::plan_overlay("home-lan", None, geth_overlay::OVERLAY_ALPN) .expect("overlay plan"), network: geth_overlay::membership_status( &geth_overlay::joined_overlay_membership( "home-lan", None, "node:local", "invite", 10, ) .expect("overlay membership"), ), enabled: true, note: "membership persisted".to_owned(), }, }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::OverlayRuntimeStarted { status: geth_overlay::OverlayRuntimeStatus { name: "home-lan".to_owned(), interface_name: "geth-homelan".to_owned(), virtual_ip: "172.22.0.10".to_owned(), cidr: "172.22.0.0/24".to_owned(), mtu: 1280, started_at_ms: 12, packets_from_tun: 1, packets_to_tun: 2, packets_to_peers: 3, last_error: None, note: "active".to_owned(), }, }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::OverlayPackets { name: "home-lan".to_owned(), packets: vec![geth_overlay::OverlayPacket { id: "overlay-packet:test".to_owned(), network: "home-lan".to_owned(), source_node: "node:peer".to_owned(), destination_node: "node:local".to_owned(), packet_base64: "RQAAFAAAQABAAQAAqBYAAawWAAI=".to_owned(), size_bytes: 20, received_at_ms: 11, note: "received".to_owned(), }], drained: true, note: "packets".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::NodeList { nodes: vec![NodeRecord { id: geth_types::NodeId::new("node:local"), device: geth_types::DeviceId::new("device:local"), name: "work-laptop".to_owned(), endpoints: Vec::new(), }], note: "reduced keychain view".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::KeychainSynced { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), ops_imported: 2, signatures_imported: 2, invalid_ops_rejected: 1, high_water_ms: 42, note: "trusted admin signatures only".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::NodeEnrollmentSynced { owner_node: "owner-laptop".to_owned(), keychain_ops_imported: 3, keychain_signatures_imported: 3, auth_ops_imported: 1, auth_signatures_imported: 1, invalid_ops_rejected: 0, note: "synced".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::AuthSynced { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), ops_imported: 1, signatures_imported: 1, invalid_ops_rejected: 0, high_water_ms: 42, note: "trusted admin signatures only".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::SyncStatus { peers: vec![SyncPeerStatus { peer_node_id: "node:peer".to_owned(), streams: vec![SyncStreamStatus { stream: "keychain".to_owned(), cursor_ms: 42, state: "ok".to_owned(), stale: false, stale_after_ms: 120_000, last_attempt_ms: Some(43), last_success_ms: Some(43), last_error: None, last_imported: 2, last_rejected: 0, next_action: "none".to_owned(), }], }], note: "local health".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::SyncRan { peers: vec![SyncPeerRun { peer_node_id: "node:peer".to_owned(), streams: vec![SyncStreamRun { stream: "auth".to_owned(), attempted: true, success: true, imported: 1, rejected: 0, cursor_ms: 44, error: None, }], }], note: "ran".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::SecretBearerVerify { secret: "bearer:test".to_owned(), resource: "resource:cas:local".to_owned(), capabilities: vec!["cas.fetch".to_owned()], nonce: "nonce".to_owned(), response: "response".to_owned(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::SecretBearerVerified { secret: "bearer:test".to_owned(), resource: "resource:cas:local".to_owned(), capabilities: vec!["cas.fetch".to_owned()], verified: true, reason: "ok".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::CasFetch { node: "node:peer".to_owned(), hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(), bearer_secret: Some("bearer:test".to_owned()), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::CasProviders { hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::CasProviders { hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(), providers: vec![CasProvider { peer_node_id: "node:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), last_seen_ms: 42, }], }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::SshRevocationExport { out: PathBuf::from("revocations.krl-spec"), format: "openssh-krl-spec".to_owned(), ca_public: None, subject: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::SshRevocationImport { path: PathBuf::from("revocations.jsonl"), format: "jsonl".to_owned(), subject: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::SshCertSync { node: "node:ca".to_owned(), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::SshRevocationSync { node: "node:ca".to_owned(), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::SshRevocationExported { out: PathBuf::from("revocations.krl-spec"), format: "openssh-krl-spec".to_owned(), count: 2, note: "OpenSSH KRL specification".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::SshCertSynced { peer_node_id: "node:ca".to_owned(), peer_agent_id: "agent:ca".to_owned(), endpoint_id: "endpoint:ca".to_owned(), requests_imported: 1, certificates_imported: 0, allowed: true, reason: "direct grant".to_owned(), note: "cert sync".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::SshRevocationSynced { peer_node_id: "node:ca".to_owned(), peer_agent_id: "agent:ca".to_owned(), endpoint_id: "endpoint:ca".to_owned(), revocations_imported: 2, allowed: true, reason: "direct grant".to_owned(), note: "revocation sync".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::KvSync { node: "node:peer".to_owned(), name: "prefs".to_owned(), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::KvSynced { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), name: "prefs".to_owned(), entries_imported: 1, allowed: true, reason: "direct grant".to_owned(), note: "kv sync".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::DocumentSet { name: "notes".to_owned(), state_json: r#"{"title":"notes"}"#.to_owned(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::PipeListen { name: "inbox".to_owned(), node: Some("node:peer".to_owned()), bearer_secret: Some("bearer:test".to_owned()), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::PipeConnect { target: "inbox".to_owned(), node: Some("node:peer".to_owned()), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::PipeSend { target: "inbox".to_owned(), data_base64: "aGVsbG8=".to_owned(), node: Some("node:peer".to_owned()), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::PipeRecv { name: "inbox".to_owned(), peek: true, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::PipeMessages { name: "inbox".to_owned(), messages: vec![PipeMessage { pipe: "inbox".to_owned(), data_base64: "aGVsbG8=".to_owned(), received_at: geth_types::UnixMillis(1), source_node: Some("node:peer".to_owned()), note: "pipe".to_owned(), }], drained: false, note: "pipe messages".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::PubsubPub { topic: "presence/test".to_owned(), message: "online".to_owned(), node: Some("node:peer".to_owned()), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::PubsubSub { topic: "presence/test".to_owned(), node: Some("node:peer".to_owned()), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::PubsubRemotePublished { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), message: PubsubMessage { topic: "presence/test".into(), message: "online".to_owned(), published_at: geth_types::UnixMillis(1), }, allowed: true, reason: "direct grant".to_owned(), note: "lossy".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::PubsubRemoteMessages { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), topic: "presence/test".to_owned(), messages: vec![PubsubMessage { topic: "presence/test".into(), message: "online".to_owned(), published_at: geth_types::UnixMillis(1), }], allowed: true, reason: "direct grant".to_owned(), note: "lossy".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::PipeRemoteConnected { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), connection: PipeConnection { target: "inbox".to_owned(), connected_at: geth_types::UnixMillis(1), local_listener_found: true, note: "remote".to_owned(), }, allowed: true, reason: "direct grant".to_owned(), note: "pipe connect".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::PipeRemoteListening { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), listener: Some(PipeListener { id: "pipe:inbox".into(), name: "inbox".to_owned(), listened_at: geth_types::UnixMillis(1), note: "remote".to_owned(), }), allowed: true, reason: "direct grant".to_owned(), note: "pipe listen".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::SshProxyConnect { node: "node:peer".to_owned(), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::SshProxyStream { node: "node:peer".to_owned(), bearer_secret: Some("bearer:test".to_owned()), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::SshAdminShell { node: "node:peer".to_owned(), command: "status".to_owned(), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::SshProxyConnected { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), connection: Some(SshProxyConnection { target_node: "node:peer".into(), connected_at: geth_types::UnixMillis(1), local_sshd_target: Some("127.0.0.1:22".to_owned()), admin_shell_available: false, note: "proxy".to_owned(), }), allowed: true, reason: "direct grant".to_owned(), note: "ssh proxy".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let response = ControlResponse::SshAdminShellOutput { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), command: "status".to_owned(), output: "node_id=node:peer".to_owned(), allowed: true, reason: "direct grant".to_owned(), note: "restricted admin shell".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::DbChanges { name: "notes".to_owned(), after_db_version: Some(7), limit: 10, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::DbSync { node: "node:peer".to_owned(), name: "notes".to_owned(), limit: 50, bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::DbSynced { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), name: "notes".to_owned(), changes_received: 1, changes_applied: 1, max_db_version: Some(7), schema_match: true, allowed: true, reason: "direct grant".to_owned(), note: "exchange only".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::CasRootScan { name: "notes".to_owned(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::CasRootSync { node: "node:peer".to_owned(), name: "notes".to_owned(), bearer_secret: None, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::CasRootSynced { peer_node_id: "node:peer".to_owned(), peer_agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), name: "notes".to_owned(), root: None, tree_bytes_imported: false, sync_conflicts: Vec::new(), allowed: false, reason: "no grant".to_owned(), note: "file-root sync".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::CasRootApply { source: "remote-node-peer-notes".to_owned(), target: PathBuf::from("/tmp/notes"), dry_run: true, }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = ControlResponse::CasRootApplied { source: "remote-node-peer-notes".to_owned(), target: PathBuf::from("/tmp/notes"), files_written: 1, dirs_created: 1, conflicts: Vec::new(), dry_run: true, note: "safe apply".to_owned(), }; assert_eq!( decode_response(&encode_response(&response).expect("encode")).expect("decode"), response ); let request = ControlRequest::CasConflictResolve { conflict_id: "file-conflict:notes:1".to_owned(), resolution: "keep-local".to_owned(), note: Some("local file is authoritative".to_owned()), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::PeerCardExport { out: None }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::PeerPing { node: "node:peer".to_owned(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let request = ControlRequest::PeerAuthCheck { node: "node:peer".to_owned(), resource: "resource:cas:local".to_owned(), capability: "cas.fetch".to_owned(), }; assert_eq!( decode_request(&encode_request(&request).expect("encode")).expect("decode"), request ); let response = PeerControlResponse::Pong { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), alpn: "/geth/control/1".to_owned(), nonce: "nonce".to_owned(), note: "candidate only".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::AuthChecked { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), resource: "resource:cas:local".to_owned(), capability: "cas.fetch".to_owned(), allowed: false, reason: "no grant".to_owned(), evaluated_ops: 0, nonce: "nonce".to_owned(), note: "protected".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::SyncStatus { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), watermarks: vec![SyncWatermark { stream: "kv:prefs".to_owned(), high_water: 42, }], nonce: "nonce".to_owned(), note: "status".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::CasFetched { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(), size_bytes: 5, content_base64: Some("aGVsbG8".to_owned()), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "cas fetch".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::CasRootSynced { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), name: "notes".to_owned(), root: None, tree_content_base64: None, allowed: false, reason: "no grant".to_owned(), evaluated_ops: 0, nonce: "nonce".to_owned(), note: "file-root sync".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let request = PeerControlRequest::DbSync { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, name: "notes".to_owned(), after_db_version: Some(7), limit: 10, nonce: "nonce".to_owned(), bearer_proof: None, }; assert_eq!( decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"), request ); let request = PeerControlRequest::SyncStatus { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, nonce: "nonce".to_owned(), }; assert_eq!( decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"), request ); let response = PeerControlResponse::SshCertSynced { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), requests: Vec::new(), certificates: Vec::new(), log_entries: Vec::new(), high_water_ms: 42, allowed: false, reason: "no grant".to_owned(), evaluated_ops: 0, nonce: "nonce".to_owned(), note: "cert sync".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::SshRevocationSynced { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), revocations: Vec::new(), log_entries: Vec::new(), high_water_ms: 42, allowed: false, reason: "no grant".to_owned(), evaluated_ops: 0, nonce: "nonce".to_owned(), note: "revocation sync".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::KvSynced { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), name: "prefs".to_owned(), entries: vec![KvSyncEntry { key: "apps/foo/theme".to_owned(), value: "dark".to_owned(), updated_at_ms: 42, }], high_water_ms: 43, docs_ticket: Some("doc-ticket".to_owned()), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "kv sync".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::PubsubPublished { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), message: Some(PubsubMessage { topic: "presence/test".into(), message: "online".to_owned(), published_at: geth_types::UnixMillis(1), }), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "lossy".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let request = PeerControlRequest::PubsubSubscribe { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, topic: "presence/test".to_owned(), nonce: "nonce".to_owned(), bearer_proof: None, }; assert_eq!( decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"), request ); let response = PeerControlResponse::PubsubSubscribed { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), topic: "presence/test".to_owned(), messages: vec![PubsubMessage { topic: "presence/test".into(), message: "online".to_owned(), published_at: geth_types::UnixMillis(1), }], allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "lossy".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::PipeConnected { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), connection: Some(PipeConnection { target: "inbox".to_owned(), connected_at: geth_types::UnixMillis(1), local_listener_found: true, note: "remote".to_owned(), }), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "pipe connect".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let request = PeerControlRequest::SshProxyConnect { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, nonce: "nonce".to_owned(), bearer_proof: Some(BearerProof { secret: "bearer:test".into(), resource: "resource:ssh-proxy:local".into(), capabilities: vec!["ssh_proxy.connect".into()], nonce: "nonce".to_owned(), response: "response".to_owned(), }), }; assert_eq!( decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"), request ); let request = PeerControlRequest::SshAdminShell { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, command: "status".to_owned(), nonce: "nonce".to_owned(), bearer_proof: Some(BearerProof { secret: "bearer:test".into(), resource: "resource:ssh-proxy:local".into(), capabilities: vec!["ssh_proxy.admin_shell".into()], nonce: "nonce".to_owned(), response: "response".to_owned(), }), }; assert_eq!( decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"), request ); let request = PeerControlRequest::PipeListen { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, name: "inbox".to_owned(), nonce: "nonce".to_owned(), bearer_proof: None, }; assert_eq!( decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"), request ); let request = PipeWireRequest::Send { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, target: "inbox".to_owned(), data_base64: "aGVsbG8=".to_owned(), nonce: "nonce".to_owned(), bearer_proof: None, }; assert_eq!( decode_pipe_wire_request(&encode_pipe_wire_request(&request).expect("encode")) .expect("decode"), request ); let response = PipeWireResponse::Sent { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), message: Box::new(Some(PipeMessage { pipe: "inbox".to_owned(), data_base64: "aGVsbG8=".to_owned(), received_at: geth_types::UnixMillis(1), source_node: Some("node:caller".to_owned()), note: "pipe".to_owned(), })), listener_found: true, allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "pipe wire".to_owned(), }; assert_eq!( decode_pipe_wire_response(&encode_pipe_wire_response(&response).expect("encode")) .expect("decode"), response ); let request = OverlayWireRequest::Packet { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, network: "home".to_owned(), packet_base64: "RQAAFAAAQABAAQAAqBYAAawWAAI=".to_owned(), nonce: "nonce".to_owned(), bearer_proof: Some(BearerProof { secret: "bearer:test".into(), resource: "resource:overlay:home".into(), capabilities: vec!["overlay.route".into()], nonce: "nonce".to_owned(), response: "response".to_owned(), }), }; assert_eq!( decode_overlay_wire_request(&encode_overlay_wire_request(&request).expect("encode")) .expect("decode"), request ); let response = OverlayWireResponse::PacketAccepted { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), packet: Some(Box::new(geth_overlay::OverlayPacket { id: "overlay-packet:test".to_owned(), network: "home".to_owned(), source_node: "node:caller".to_owned(), destination_node: "node:peer".to_owned(), packet_base64: "RQAAFAAAQABAAQAAqBYAAawWAAI=".to_owned(), size_bytes: 20, received_at_ms: 12, note: "overlay".to_owned(), })), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "overlay wire".to_owned(), }; assert_eq!( decode_overlay_wire_response(&encode_overlay_wire_response(&response).expect("encode")) .expect("decode"), response ); let request = PipeWireRequest::TcpConnect { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, target_addr: "127.0.0.1:22".to_owned(), nonce: "nonce".to_owned(), bearer_proof: Some(BearerProof { secret: "bearer:test".into(), resource: "resource:pipe-tcp:127.0.0.1:22".into(), capabilities: vec!["pipe.forward".into()], nonce: "nonce".to_owned(), response: "response".to_owned(), }), }; assert_eq!( decode_pipe_wire_request(&encode_pipe_wire_request(&request).expect("encode")) .expect("decode"), request ); let response = PipeWireResponse::Connected { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), connection: Some(PipeConnection { target: "127.0.0.1:22".to_owned(), connected_at: geth_types::UnixMillis(1), local_listener_found: true, note: "tcp".to_owned(), }), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "pipe tcp".to_owned(), }; assert_eq!( decode_pipe_wire_response(&encode_pipe_wire_response(&response).expect("encode")) .expect("decode"), response ); let request = PipeWireRequest::UnixConnect { peer_card: PeerCard { node_id: "node:caller".into(), agent_id: "agent:caller".into(), endpoints: Vec::new(), issued_at: geth_types::UnixMillis(1), signature: geth_discovery::SignatureMetadata { namespace: "geth.peer-card.v1@geth.local".to_owned(), signer: "agent:caller".to_owned(), public_key: "key".to_owned(), signature: "sig".to_owned(), }, }, target_path: PathBuf::from("/tmp/geth-test.sock"), nonce: "nonce".to_owned(), bearer_proof: None, }; assert_eq!( decode_pipe_wire_request(&encode_pipe_wire_request(&request).expect("encode")) .expect("decode"), request ); let response = PipeWireResponse::Error { message: "pipe denied".to_owned(), }; assert_eq!( decode_pipe_wire_response(&encode_pipe_wire_response(&response).expect("encode")) .expect("decode"), response ); let response = OverlayWireResponse::Error { message: "overlay denied".to_owned(), }; assert_eq!( decode_overlay_wire_response(&encode_overlay_wire_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::SshProxyConnected { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), connection: Some(SshProxyConnection { target_node: "node:peer".into(), connected_at: geth_types::UnixMillis(1), local_sshd_target: Some("127.0.0.1:22".to_owned()), admin_shell_available: false, note: "proxy".to_owned(), }), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "ssh proxy".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::SshAdminShellOutput { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), command: "status".to_owned(), output: "node_id=node:peer".to_owned(), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "restricted admin shell".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); let response = PeerControlResponse::DbSynced { node_id: "node:peer".to_owned(), agent_id: "agent:peer".to_owned(), endpoint_id: "endpoint:peer".to_owned(), remote_endpoint_id: "endpoint:caller".to_owned(), name: "notes".to_owned(), batch: Some(CrSqliteChangeBatch { schema_metadata: "tables=1 schema_hash=abc".to_owned(), max_db_version: Some(7), changes: Vec::new(), }), high_water_db_version: Some(7), allowed: true, reason: "direct grant".to_owned(), evaluated_ops: 1, nonce: "nonce".to_owned(), note: "db sync".to_owned(), }; assert_eq!( decode_peer_response(&encode_peer_response(&response).expect("encode")) .expect("decode"), response ); } #[test] fn malformed_and_unknown_protocol_inputs_fail() { for input in [ "{", "{}", r#"{"type":"unknown"}"#, r#"{"type":"status","extra":}"#, ] { assert!(decode_request(input).is_err()); assert!(decode_response(input).is_err()); assert!(decode_peer_request(input).is_err()); assert!(decode_peer_response(input).is_err()); assert!(decode_pipe_wire_request(input).is_err()); assert!(decode_pipe_wire_response(input).is_err()); assert!(decode_overlay_wire_request(input).is_err()); assert!(decode_overlay_wire_response(input).is_err()); } } }