2026-05-21 18:01:38 +02:00
|
|
|
use geth_auth::{AuthExplanation, AuthOp, AuthOpSignature};
|
2026-05-18 03:57:26 +02:00
|
|
|
use geth_cas::{FileConflict, FileRoot, FileRootScan};
|
2026-05-17 20:32:36 +02:00
|
|
|
use geth_db::{CrSqliteChangeBatch, DbResource};
|
2026-05-18 04:03:52 +02:00
|
|
|
use geth_discovery::{DiscoveredPeer, PeerCard};
|
2026-05-17 19:59:03 +02:00
|
|
|
use geth_document::{DocumentResource, DocumentState};
|
2026-05-21 18:01:38 +02:00
|
|
|
use geth_keychain::{KeychainOp, KeychainOpSignature, NodeEnrollmentRequest, NodeRecord};
|
2026-05-18 18:36:03 +02:00
|
|
|
use geth_kv::{KvEntry, KvResource, KvSyncEntry};
|
2026-05-23 01:17:30 +02:00
|
|
|
use geth_overlay::{OverlayJoinPlan, OverlayNetworkStatus, OverlayPlan};
|
2026-05-20 13:57:14 +02:00
|
|
|
use geth_pipe::{PipeConnection, PipeListener, PipeMessage};
|
2026-05-17 18:23:51 +02:00
|
|
|
use geth_pubsub::PubsubMessage;
|
2026-05-15 15:08:20 +02:00
|
|
|
use geth_resource::ResourceDescriptor;
|
2026-05-19 19:08:08 +02:00
|
|
|
use geth_secrets::{BearerAccess, BearerChallenge, BearerProof, ResourceMasterSecret};
|
2026-05-16 00:17:08 +02:00
|
|
|
use geth_ssh_identity::{
|
2026-05-22 16:51:30 +02:00
|
|
|
SshCertApproval, SshCertRequest, SshCertificateRecord, SshDistributionLogEntry,
|
|
|
|
|
SshRevocationEntry,
|
2026-05-16 00:17:08 +02:00
|
|
|
};
|
2026-05-19 15:51:11 +02:00
|
|
|
use geth_ssh_proxy::SshProxyConnection;
|
2026-05-15 15:08:20 +02:00
|
|
|
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,
|
2026-05-18 04:03:52 +02:00
|
|
|
PeerCardExport {
|
|
|
|
|
out: Option<PathBuf>,
|
|
|
|
|
},
|
|
|
|
|
PeerCardImport {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
|
|
|
|
PeerCardList,
|
2026-05-18 12:09:50 +02:00
|
|
|
PeerPing {
|
|
|
|
|
node: String,
|
|
|
|
|
},
|
2026-05-18 17:01:50 +02:00
|
|
|
PeerAuthCheck {
|
|
|
|
|
node: String,
|
|
|
|
|
resource: String,
|
|
|
|
|
capability: String,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
ResourceList,
|
|
|
|
|
ResourceCreate {
|
|
|
|
|
kind: String,
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
2026-05-23 01:17:30 +02:00
|
|
|
OverlayStatus,
|
|
|
|
|
OverlayPlan {
|
|
|
|
|
name: String,
|
|
|
|
|
cidr: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
OverlayJoin {
|
|
|
|
|
name: String,
|
|
|
|
|
secret: String,
|
|
|
|
|
cidr: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
OverlayLeave {
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
CasAdd {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
2026-05-21 01:35:00 +02:00
|
|
|
CasAddPrivate {
|
|
|
|
|
resource: String,
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
CasGet {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
out: PathBuf,
|
|
|
|
|
},
|
2026-05-21 01:35:00 +02:00
|
|
|
CasGetPrivate {
|
|
|
|
|
resource: String,
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
out: PathBuf,
|
|
|
|
|
},
|
2026-05-18 17:18:25 +02:00
|
|
|
CasFetch {
|
|
|
|
|
node: String,
|
|
|
|
|
hash: BlobHash,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-18 17:18:25 +02:00
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
CasHash {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
|
|
|
|
CasHas {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
},
|
2026-05-16 16:36:35 +02:00
|
|
|
CasPin {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
},
|
|
|
|
|
CasUnpin {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
},
|
2026-05-16 21:10:25 +02:00
|
|
|
CasCleanup {
|
|
|
|
|
dry_run: bool,
|
|
|
|
|
},
|
2026-05-19 15:37:02 +02:00
|
|
|
CasProviders {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
CasList,
|
2026-05-18 03:50:09 +02:00
|
|
|
CasRootAdd {
|
|
|
|
|
name: String,
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
|
|
|
|
CasRootList,
|
|
|
|
|
CasRootScan {
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
2026-05-20 13:22:40 +02:00
|
|
|
CasRootSync {
|
|
|
|
|
node: String,
|
|
|
|
|
name: String,
|
|
|
|
|
bearer_secret: Option<String>,
|
|
|
|
|
},
|
2026-05-20 13:30:57 +02:00
|
|
|
CasRootApply {
|
|
|
|
|
source: String,
|
|
|
|
|
target: PathBuf,
|
|
|
|
|
dry_run: bool,
|
|
|
|
|
},
|
2026-05-18 03:57:26 +02:00
|
|
|
CasConflictRecord {
|
|
|
|
|
root: String,
|
|
|
|
|
path: String,
|
|
|
|
|
kind: String,
|
|
|
|
|
detail: String,
|
|
|
|
|
base_tree: Option<BlobHash>,
|
|
|
|
|
local_tree: Option<BlobHash>,
|
|
|
|
|
remote_tree: Option<BlobHash>,
|
|
|
|
|
},
|
|
|
|
|
CasConflictList {
|
|
|
|
|
root: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
CasConflictResolve {
|
|
|
|
|
conflict_id: String,
|
|
|
|
|
resolution: String,
|
|
|
|
|
note: Option<String>,
|
|
|
|
|
},
|
2026-05-21 11:29:29 +02:00
|
|
|
NodeList,
|
|
|
|
|
NodeRename {
|
|
|
|
|
node: String,
|
|
|
|
|
name: String,
|
|
|
|
|
signing_key_path: Option<PathBuf>,
|
|
|
|
|
},
|
|
|
|
|
NodeRevoke {
|
|
|
|
|
node: String,
|
|
|
|
|
signing_key_path: Option<PathBuf>,
|
|
|
|
|
},
|
|
|
|
|
NodeGrant {
|
|
|
|
|
node: String,
|
|
|
|
|
resource: String,
|
|
|
|
|
capability: String,
|
|
|
|
|
grant_id: Option<String>,
|
2026-05-21 18:15:10 +02:00
|
|
|
signing_key_path: Option<PathBuf>,
|
|
|
|
|
admin_key_path: Option<PathBuf>,
|
2026-05-21 11:29:29 +02:00
|
|
|
},
|
|
|
|
|
NodeRevokeGrant {
|
|
|
|
|
resource: String,
|
|
|
|
|
grant_id: String,
|
2026-05-21 18:15:10 +02:00
|
|
|
signing_key_path: Option<PathBuf>,
|
|
|
|
|
admin_key_path: Option<PathBuf>,
|
|
|
|
|
},
|
|
|
|
|
NodeEndpointAdd {
|
|
|
|
|
node: String,
|
|
|
|
|
endpoint: String,
|
|
|
|
|
signing_key_path: Option<PathBuf>,
|
|
|
|
|
},
|
|
|
|
|
NodeEndpointRevoke {
|
|
|
|
|
node: String,
|
|
|
|
|
endpoint: String,
|
|
|
|
|
signing_key_path: Option<PathBuf>,
|
2026-05-21 11:29:29 +02:00
|
|
|
},
|
2026-05-21 18:01:38 +02:00
|
|
|
NodeEnrollRequest {
|
|
|
|
|
node_name: String,
|
|
|
|
|
capabilities: Vec<String>,
|
|
|
|
|
reason: Option<String>,
|
|
|
|
|
out: Option<PathBuf>,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollSubmit {
|
|
|
|
|
owner_node: String,
|
|
|
|
|
request_id: Option<String>,
|
|
|
|
|
path: Option<PathBuf>,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollImport {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollList {
|
|
|
|
|
status: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollApprove {
|
|
|
|
|
request_id: String,
|
|
|
|
|
signing_key_path: PathBuf,
|
|
|
|
|
admin_key_path: Option<PathBuf>,
|
|
|
|
|
node_name: Option<String>,
|
|
|
|
|
capabilities: Vec<String>,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollSync {
|
|
|
|
|
owner_node: String,
|
|
|
|
|
},
|
2026-05-16 16:34:20 +02:00
|
|
|
KeychainInit {
|
|
|
|
|
admin_key_path: Option<PathBuf>,
|
2026-05-19 16:04:20 +02:00
|
|
|
signing_key_path: Option<PathBuf>,
|
2026-05-16 16:34:20 +02:00
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
KeychainStatus,
|
2026-05-21 11:29:29 +02:00
|
|
|
KeychainSync {
|
|
|
|
|
node: String,
|
|
|
|
|
},
|
2026-05-21 18:01:38 +02:00
|
|
|
AuthSync {
|
|
|
|
|
node: String,
|
|
|
|
|
},
|
2026-05-21 19:31:05 +02:00
|
|
|
SyncStatus,
|
|
|
|
|
SyncNow {
|
|
|
|
|
node: Option<String>,
|
|
|
|
|
},
|
2026-05-16 22:18:49 +02:00
|
|
|
SecretStatus,
|
|
|
|
|
SecretCreate {
|
|
|
|
|
resource: String,
|
|
|
|
|
},
|
|
|
|
|
SecretRotate {
|
|
|
|
|
resource: String,
|
|
|
|
|
},
|
2026-05-17 02:58:58 +02:00
|
|
|
SecretBearerCreate {
|
|
|
|
|
resource: String,
|
|
|
|
|
capabilities: Vec<String>,
|
|
|
|
|
expires_at_ms: Option<i64>,
|
|
|
|
|
},
|
|
|
|
|
SecretBearerList,
|
2026-05-19 19:08:08 +02:00
|
|
|
SecretBearerChallenge {
|
|
|
|
|
resource: String,
|
|
|
|
|
capabilities: Vec<String>,
|
|
|
|
|
},
|
|
|
|
|
SecretBearerProve {
|
|
|
|
|
secret: String,
|
|
|
|
|
resource: String,
|
|
|
|
|
capabilities: Vec<String>,
|
|
|
|
|
nonce: String,
|
|
|
|
|
},
|
|
|
|
|
SecretBearerVerify {
|
|
|
|
|
secret: String,
|
|
|
|
|
resource: String,
|
|
|
|
|
capabilities: Vec<String>,
|
|
|
|
|
nonce: String,
|
|
|
|
|
response: String,
|
|
|
|
|
},
|
2026-05-17 02:58:58 +02:00
|
|
|
SecretBearerRevoke {
|
|
|
|
|
resource: String,
|
|
|
|
|
secret: String,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
AuthExplain {
|
|
|
|
|
subject: String,
|
|
|
|
|
resource: String,
|
|
|
|
|
capability: String,
|
|
|
|
|
},
|
2026-05-16 16:32:03 +02:00
|
|
|
AuthGrant {
|
|
|
|
|
subject: String,
|
|
|
|
|
resource: String,
|
|
|
|
|
capability: String,
|
|
|
|
|
grant_id: Option<String>,
|
2026-05-21 18:15:10 +02:00
|
|
|
signing_key_path: Option<PathBuf>,
|
|
|
|
|
admin_key_path: Option<PathBuf>,
|
2026-05-16 16:32:03 +02:00
|
|
|
},
|
|
|
|
|
AuthRevoke {
|
|
|
|
|
resource: String,
|
|
|
|
|
grant_id: String,
|
2026-05-21 18:15:10 +02:00
|
|
|
signing_key_path: Option<PathBuf>,
|
|
|
|
|
admin_key_path: Option<PathBuf>,
|
2026-05-16 16:32:03 +02:00
|
|
|
},
|
2026-05-16 00:17:08 +02:00
|
|
|
SshCertRequest {
|
|
|
|
|
public_key_path: PathBuf,
|
|
|
|
|
cert_kind: String,
|
|
|
|
|
principals: Vec<String>,
|
|
|
|
|
requested_validity: Option<String>,
|
|
|
|
|
renewal_of: Option<String>,
|
|
|
|
|
reason: Option<String>,
|
2026-05-19 15:44:13 +02:00
|
|
|
subject: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
SshCertRequests {
|
|
|
|
|
subject: Option<String>,
|
2026-05-16 00:17:08 +02:00
|
|
|
},
|
|
|
|
|
SshCertApprove {
|
|
|
|
|
request_id: String,
|
|
|
|
|
ca_key_path: PathBuf,
|
|
|
|
|
valid_for: Option<String>,
|
|
|
|
|
serial: Option<u64>,
|
|
|
|
|
out: Option<PathBuf>,
|
2026-05-19 15:56:47 +02:00
|
|
|
sign: bool,
|
2026-05-19 15:44:13 +02:00
|
|
|
subject: Option<String>,
|
2026-05-16 00:17:08 +02:00
|
|
|
},
|
|
|
|
|
SshCertImport {
|
|
|
|
|
request_id: String,
|
|
|
|
|
cert_path: PathBuf,
|
2026-05-19 15:44:13 +02:00
|
|
|
subject: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
SshCertList {
|
|
|
|
|
subject: Option<String>,
|
2026-05-16 00:17:08 +02:00
|
|
|
},
|
2026-05-18 17:24:10 +02:00
|
|
|
SshCertSync {
|
|
|
|
|
node: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-18 17:24:10 +02:00
|
|
|
},
|
2026-05-16 00:17:08 +02:00
|
|
|
SshRevocationAdd {
|
|
|
|
|
kind: String,
|
|
|
|
|
target: String,
|
|
|
|
|
reason: Option<String>,
|
2026-05-19 15:44:13 +02:00
|
|
|
subject: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
SshRevocationList {
|
|
|
|
|
subject: Option<String>,
|
2026-05-16 00:17:08 +02:00
|
|
|
},
|
|
|
|
|
SshRevocationExport {
|
|
|
|
|
out: PathBuf,
|
2026-05-17 18:29:47 +02:00
|
|
|
format: String,
|
2026-05-18 11:51:12 +02:00
|
|
|
ca_public: Option<PathBuf>,
|
2026-05-19 15:44:13 +02:00
|
|
|
subject: Option<String>,
|
2026-05-16 00:17:08 +02:00
|
|
|
},
|
2026-05-18 11:56:42 +02:00
|
|
|
SshRevocationImport {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
format: String,
|
2026-05-19 15:44:13 +02:00
|
|
|
subject: Option<String>,
|
2026-05-18 11:56:42 +02:00
|
|
|
},
|
2026-05-18 17:24:10 +02:00
|
|
|
SshRevocationSync {
|
|
|
|
|
node: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-18 17:24:10 +02:00
|
|
|
},
|
2026-05-19 15:51:11 +02:00
|
|
|
SshProxyConnect {
|
|
|
|
|
node: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-19 15:51:11 +02:00
|
|
|
},
|
2026-05-21 01:03:38 +02:00
|
|
|
SshProxyStream {
|
|
|
|
|
node: String,
|
|
|
|
|
bearer_secret: Option<String>,
|
|
|
|
|
},
|
2026-05-21 01:49:48 +02:00
|
|
|
SshAdminShell {
|
|
|
|
|
node: String,
|
|
|
|
|
command: String,
|
|
|
|
|
bearer_secret: Option<String>,
|
|
|
|
|
},
|
2026-05-16 21:13:33 +02:00
|
|
|
DbAdd {
|
|
|
|
|
name: String,
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
|
|
|
|
DbStatus {
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
2026-05-17 20:32:36 +02:00
|
|
|
DbChanges {
|
|
|
|
|
name: String,
|
|
|
|
|
after_db_version: Option<i64>,
|
|
|
|
|
limit: u32,
|
|
|
|
|
},
|
2026-05-18 22:11:57 +02:00
|
|
|
DbSync {
|
|
|
|
|
node: String,
|
|
|
|
|
name: String,
|
|
|
|
|
limit: u32,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-18 22:11:57 +02:00
|
|
|
},
|
2026-05-16 21:52:35 +02:00
|
|
|
KvCreate {
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
|
|
|
|
KvSet {
|
|
|
|
|
name: String,
|
|
|
|
|
key: String,
|
|
|
|
|
value: String,
|
2026-05-18 04:06:41 +02:00
|
|
|
subject: Option<String>,
|
2026-05-16 21:52:35 +02:00
|
|
|
},
|
|
|
|
|
KvGet {
|
|
|
|
|
name: String,
|
|
|
|
|
key: String,
|
|
|
|
|
},
|
2026-05-18 18:36:03 +02:00
|
|
|
KvSync {
|
|
|
|
|
node: String,
|
|
|
|
|
name: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-18 18:36:03 +02:00
|
|
|
},
|
2026-05-16 22:15:18 +02:00
|
|
|
DocumentCreate {
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
|
|
|
|
DocumentStatus {
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
2026-05-17 19:59:03 +02:00
|
|
|
DocumentSet {
|
|
|
|
|
name: String,
|
|
|
|
|
state_json: String,
|
|
|
|
|
},
|
|
|
|
|
DocumentGet {
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
2026-05-18 18:49:34 +02:00
|
|
|
DocumentSync {
|
|
|
|
|
node: String,
|
|
|
|
|
name: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-18 18:49:34 +02:00
|
|
|
},
|
2026-05-17 18:23:51 +02:00
|
|
|
PubsubPub {
|
|
|
|
|
topic: String,
|
|
|
|
|
message: String,
|
2026-05-18 18:41:04 +02:00
|
|
|
node: Option<String>,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-17 18:23:51 +02:00
|
|
|
},
|
|
|
|
|
PubsubSub {
|
|
|
|
|
topic: String,
|
2026-05-19 15:28:48 +02:00
|
|
|
node: Option<String>,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-17 18:23:51 +02:00
|
|
|
},
|
2026-05-17 20:17:26 +02:00
|
|
|
PipeListen {
|
|
|
|
|
name: String,
|
2026-05-19 19:21:42 +02:00
|
|
|
node: Option<String>,
|
|
|
|
|
bearer_secret: Option<String>,
|
2026-05-17 20:17:26 +02:00
|
|
|
},
|
|
|
|
|
PipeConnect {
|
|
|
|
|
target: String,
|
2026-05-18 18:45:10 +02:00
|
|
|
node: Option<String>,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Option<String>,
|
2026-05-17 20:17:26 +02:00
|
|
|
},
|
2026-05-21 01:12:01 +02:00
|
|
|
PipeTcpForward {
|
|
|
|
|
node: String,
|
|
|
|
|
listen_addr: String,
|
|
|
|
|
target_addr: String,
|
|
|
|
|
bearer_secret: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
PipeTcpStream {
|
|
|
|
|
node: String,
|
|
|
|
|
target_addr: String,
|
|
|
|
|
bearer_secret: Option<String>,
|
|
|
|
|
},
|
2026-05-21 01:15:51 +02:00
|
|
|
PipeUnixForward {
|
|
|
|
|
node: String,
|
|
|
|
|
listen_path: PathBuf,
|
|
|
|
|
target_path: PathBuf,
|
|
|
|
|
bearer_secret: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
PipeUnixStream {
|
|
|
|
|
node: String,
|
|
|
|
|
target_path: PathBuf,
|
|
|
|
|
bearer_secret: Option<String>,
|
|
|
|
|
},
|
2026-05-20 13:57:14 +02:00
|
|
|
PipeSend {
|
|
|
|
|
target: String,
|
|
|
|
|
data_base64: String,
|
|
|
|
|
node: Option<String>,
|
|
|
|
|
bearer_secret: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
PipeRecv {
|
|
|
|
|
name: String,
|
|
|
|
|
peek: bool,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
ModuleStub {
|
|
|
|
|
module: String,
|
|
|
|
|
command: String,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 20:32:36 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
2026-05-15 15:08:20 +02:00
|
|
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
|
|
|
|
pub enum ControlResponse {
|
|
|
|
|
Status(StatusResponse),
|
|
|
|
|
NodeId(NodeIdResponse),
|
2026-05-18 04:03:52 +02:00
|
|
|
PeerCardExported {
|
|
|
|
|
card: PeerCard,
|
|
|
|
|
out: Option<PathBuf>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
PeerCardImported {
|
|
|
|
|
peer: DiscoveredPeer,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
PeerCardList {
|
|
|
|
|
peers: Vec<DiscoveredPeer>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 12:09:50 +02:00
|
|
|
PeerPinged {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
alpn: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 17:01:50 +02:00
|
|
|
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,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
ResourceList {
|
|
|
|
|
resources: Vec<ResourceDescriptor>,
|
|
|
|
|
},
|
|
|
|
|
ResourceCreated {
|
|
|
|
|
resource: ResourceDescriptor,
|
|
|
|
|
},
|
2026-05-23 01:17:30 +02:00
|
|
|
OverlayStatus {
|
|
|
|
|
networks: Vec<OverlayNetworkStatus>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
OverlayPlanned {
|
|
|
|
|
plan: OverlayPlan,
|
|
|
|
|
},
|
|
|
|
|
OverlayJoined {
|
|
|
|
|
join: OverlayJoinPlan,
|
|
|
|
|
},
|
|
|
|
|
OverlayLeft {
|
|
|
|
|
name: String,
|
|
|
|
|
stopped: bool,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
CasAdded {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
size_bytes: u64,
|
|
|
|
|
},
|
2026-05-21 01:35:00 +02:00
|
|
|
CasPrivateAdded {
|
|
|
|
|
resource: String,
|
|
|
|
|
epoch: u64,
|
|
|
|
|
plaintext_hash: BlobHash,
|
|
|
|
|
encrypted_hash: BlobHash,
|
|
|
|
|
size_bytes: u64,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
CasGot {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
out: PathBuf,
|
|
|
|
|
size_bytes: u64,
|
|
|
|
|
},
|
2026-05-21 01:35:00 +02:00
|
|
|
CasPrivateGot {
|
|
|
|
|
resource: String,
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
plaintext_hash: BlobHash,
|
|
|
|
|
out: PathBuf,
|
|
|
|
|
size_bytes: u64,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 17:18:25 +02:00
|
|
|
CasFetched {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
size_bytes: u64,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
CasHash {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
},
|
|
|
|
|
CasHas {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
present: bool,
|
|
|
|
|
},
|
2026-05-16 16:36:35 +02:00
|
|
|
CasPinned {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
pinned: bool,
|
|
|
|
|
},
|
2026-05-16 21:10:25 +02:00
|
|
|
CasCleanup {
|
|
|
|
|
removed: Vec<BlobHash>,
|
|
|
|
|
retained_pinned: Vec<BlobHash>,
|
|
|
|
|
dry_run: bool,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
CasList {
|
|
|
|
|
blobs: Vec<CasBlob>,
|
|
|
|
|
},
|
2026-05-19 15:37:02 +02:00
|
|
|
CasProviders {
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
providers: Vec<CasProvider>,
|
|
|
|
|
},
|
2026-05-18 03:50:09 +02:00
|
|
|
CasRootAdded {
|
|
|
|
|
root: FileRoot,
|
|
|
|
|
},
|
|
|
|
|
CasRootList {
|
|
|
|
|
roots: Vec<FileRoot>,
|
|
|
|
|
},
|
|
|
|
|
CasRootScanned {
|
|
|
|
|
scan: FileRootScan,
|
|
|
|
|
},
|
2026-05-20 13:22:40 +02:00
|
|
|
CasRootSynced {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
root: Option<FileRoot>,
|
|
|
|
|
tree_bytes_imported: bool,
|
2026-05-21 01:40:50 +02:00
|
|
|
sync_conflicts: Vec<FileConflict>,
|
2026-05-20 13:22:40 +02:00
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-20 13:30:57 +02:00
|
|
|
CasRootApplied {
|
|
|
|
|
source: String,
|
|
|
|
|
target: PathBuf,
|
|
|
|
|
files_written: usize,
|
|
|
|
|
dirs_created: usize,
|
|
|
|
|
conflicts: Vec<FileConflict>,
|
|
|
|
|
dry_run: bool,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 03:57:26 +02:00
|
|
|
CasConflictRecorded {
|
|
|
|
|
conflict: FileConflict,
|
|
|
|
|
},
|
|
|
|
|
CasConflictList {
|
|
|
|
|
conflicts: Vec<FileConflict>,
|
|
|
|
|
},
|
|
|
|
|
CasConflictResolved {
|
|
|
|
|
conflict: FileConflict,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
KeychainStatus(KeychainStatusResponse),
|
2026-05-16 16:34:20 +02:00
|
|
|
KeychainInitialized {
|
|
|
|
|
ops: Vec<KeychainOp>,
|
2026-05-19 16:04:20 +02:00
|
|
|
signatures: Vec<KeychainOpSignature>,
|
2026-05-16 16:34:20 +02:00
|
|
|
},
|
2026-05-21 11:29:29 +02:00
|
|
|
KeychainSynced {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
ops_imported: usize,
|
|
|
|
|
signatures_imported: usize,
|
|
|
|
|
invalid_ops_rejected: usize,
|
2026-05-21 19:31:05 +02:00
|
|
|
high_water_ms: i64,
|
2026-05-21 11:29:29 +02:00
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-21 18:01:38 +02:00
|
|
|
AuthSynced {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
ops_imported: usize,
|
|
|
|
|
signatures_imported: usize,
|
|
|
|
|
invalid_ops_rejected: usize,
|
2026-05-21 19:31:05 +02:00
|
|
|
high_water_ms: i64,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
SyncStatus {
|
|
|
|
|
peers: Vec<SyncPeerStatus>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
SyncRan {
|
|
|
|
|
peers: Vec<SyncPeerRun>,
|
2026-05-21 18:01:38 +02:00
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-16 22:18:49 +02:00
|
|
|
SecretStatus {
|
|
|
|
|
secrets: Vec<ResourceMasterSecret>,
|
|
|
|
|
},
|
|
|
|
|
SecretCreated {
|
|
|
|
|
secret: ResourceMasterSecret,
|
|
|
|
|
},
|
2026-05-17 02:58:58 +02:00
|
|
|
SecretBearerCreated {
|
|
|
|
|
access: BearerAccess,
|
|
|
|
|
},
|
|
|
|
|
SecretBearerList {
|
|
|
|
|
access: Vec<BearerAccess>,
|
|
|
|
|
},
|
2026-05-19 19:08:08 +02:00
|
|
|
SecretBearerChallenge {
|
|
|
|
|
challenge: BearerChallenge,
|
|
|
|
|
},
|
|
|
|
|
SecretBearerProof {
|
|
|
|
|
proof: BearerProof,
|
|
|
|
|
},
|
|
|
|
|
SecretBearerVerified {
|
|
|
|
|
secret: String,
|
|
|
|
|
resource: String,
|
|
|
|
|
capabilities: Vec<String>,
|
|
|
|
|
verified: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
},
|
2026-05-17 02:58:58 +02:00
|
|
|
SecretBearerRevoked {
|
|
|
|
|
resource: String,
|
|
|
|
|
secret: String,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
AuthExplain(AuthExplanation),
|
2026-05-16 16:32:03 +02:00
|
|
|
AuthOpRecorded {
|
|
|
|
|
op: AuthOp,
|
2026-05-21 18:15:10 +02:00
|
|
|
signatures: Vec<AuthOpSignature>,
|
2026-05-16 16:32:03 +02:00
|
|
|
},
|
2026-05-21 11:29:29 +02:00
|
|
|
NodeList {
|
|
|
|
|
nodes: Vec<NodeRecord>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
NodeKeychainUpdated {
|
|
|
|
|
ops: Vec<KeychainOp>,
|
|
|
|
|
signatures: Vec<KeychainOpSignature>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
NodeGrantUpdated {
|
|
|
|
|
op: AuthOp,
|
2026-05-21 18:01:38 +02:00
|
|
|
signatures: Vec<AuthOpSignature>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollmentRequested {
|
|
|
|
|
request: NodeEnrollmentRequest,
|
|
|
|
|
out: Option<PathBuf>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollmentSubmitted {
|
|
|
|
|
request_id: String,
|
|
|
|
|
owner_node_id: String,
|
|
|
|
|
accepted: bool,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollmentImported {
|
|
|
|
|
request: NodeEnrollmentRequest,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollmentList {
|
|
|
|
|
requests: Vec<NodeEnrollmentRequest>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollmentApproved {
|
|
|
|
|
request: NodeEnrollmentRequest,
|
|
|
|
|
keychain_ops: Vec<KeychainOp>,
|
|
|
|
|
keychain_signatures: Vec<KeychainOpSignature>,
|
|
|
|
|
auth_ops: Vec<AuthOp>,
|
|
|
|
|
auth_signatures: Vec<AuthOpSignature>,
|
|
|
|
|
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,
|
2026-05-21 11:29:29 +02:00
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-16 00:17:08 +02:00
|
|
|
SshCertRequested {
|
|
|
|
|
request: SshCertRequest,
|
|
|
|
|
},
|
|
|
|
|
SshCertRequests {
|
|
|
|
|
requests: Vec<SshCertRequest>,
|
|
|
|
|
},
|
|
|
|
|
SshCertApproved {
|
|
|
|
|
approval: SshCertApproval,
|
|
|
|
|
},
|
|
|
|
|
SshCertImported {
|
|
|
|
|
certificate: SshCertificateRecord,
|
|
|
|
|
},
|
|
|
|
|
SshCertList {
|
|
|
|
|
requests: Vec<SshCertRequest>,
|
|
|
|
|
certificates: Vec<SshCertificateRecord>,
|
|
|
|
|
},
|
2026-05-18 17:24:10 +02:00
|
|
|
SshCertSynced {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
requests_imported: usize,
|
|
|
|
|
certificates_imported: usize,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-16 00:17:08 +02:00
|
|
|
SshRevocationAdded {
|
|
|
|
|
revocation: SshRevocationEntry,
|
|
|
|
|
},
|
|
|
|
|
SshRevocationList {
|
|
|
|
|
revocations: Vec<SshRevocationEntry>,
|
|
|
|
|
},
|
|
|
|
|
SshRevocationExported {
|
|
|
|
|
out: PathBuf,
|
2026-05-17 18:29:47 +02:00
|
|
|
format: String,
|
2026-05-16 00:17:08 +02:00
|
|
|
count: usize,
|
2026-05-17 18:29:47 +02:00
|
|
|
note: String,
|
2026-05-16 00:17:08 +02:00
|
|
|
},
|
2026-05-18 11:56:42 +02:00
|
|
|
SshRevocationImported {
|
|
|
|
|
revocations: Vec<SshRevocationEntry>,
|
|
|
|
|
format: String,
|
|
|
|
|
count: usize,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 17:24:10 +02:00
|
|
|
SshRevocationSynced {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
revocations_imported: usize,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-16 21:13:33 +02:00
|
|
|
DbAdded {
|
|
|
|
|
db: DbResource,
|
|
|
|
|
},
|
|
|
|
|
DbStatus {
|
|
|
|
|
db: DbResource,
|
|
|
|
|
},
|
2026-05-17 20:32:36 +02:00
|
|
|
DbChanges {
|
|
|
|
|
db: DbResource,
|
|
|
|
|
batch: CrSqliteChangeBatch,
|
|
|
|
|
},
|
2026-05-18 22:11:57 +02:00
|
|
|
DbSynced {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
changes_received: usize,
|
2026-05-19 19:02:39 +02:00
|
|
|
changes_applied: usize,
|
2026-05-18 22:11:57 +02:00
|
|
|
max_db_version: Option<i64>,
|
|
|
|
|
schema_match: bool,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-16 21:52:35 +02:00
|
|
|
KvCreated {
|
|
|
|
|
kv: KvResource,
|
|
|
|
|
},
|
|
|
|
|
KvSet {
|
|
|
|
|
entry: KvEntry,
|
|
|
|
|
},
|
|
|
|
|
KvGet {
|
|
|
|
|
entry: Option<KvEntry>,
|
|
|
|
|
},
|
2026-05-18 18:36:03 +02:00
|
|
|
KvSynced {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
entries_imported: usize,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-16 22:15:18 +02:00
|
|
|
DocumentCreated {
|
|
|
|
|
document: DocumentResource,
|
|
|
|
|
},
|
|
|
|
|
DocumentStatus {
|
|
|
|
|
document: DocumentResource,
|
|
|
|
|
},
|
2026-05-17 19:59:03 +02:00
|
|
|
DocumentSet {
|
|
|
|
|
state: DocumentState,
|
|
|
|
|
},
|
|
|
|
|
DocumentGet {
|
|
|
|
|
state: DocumentState,
|
|
|
|
|
},
|
2026-05-18 18:49:34 +02:00
|
|
|
DocumentSynced {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
updated: bool,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-17 18:23:51 +02:00
|
|
|
PubsubPublished {
|
|
|
|
|
message: PubsubMessage,
|
|
|
|
|
},
|
2026-05-18 18:41:04 +02:00
|
|
|
PubsubRemotePublished {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
message: PubsubMessage,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-17 18:23:51 +02:00
|
|
|
PubsubMessages {
|
|
|
|
|
topic: String,
|
|
|
|
|
messages: Vec<PubsubMessage>,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-19 15:28:48 +02:00
|
|
|
PubsubRemoteMessages {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
topic: String,
|
|
|
|
|
messages: Vec<PubsubMessage>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-17 20:17:26 +02:00
|
|
|
PipeListening {
|
|
|
|
|
listener: PipeListener,
|
|
|
|
|
},
|
2026-05-19 19:21:42 +02:00
|
|
|
PipeRemoteListening {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
listener: Option<PipeListener>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-17 20:17:26 +02:00
|
|
|
PipeConnected {
|
|
|
|
|
connection: PipeConnection,
|
|
|
|
|
},
|
2026-05-18 18:45:10 +02:00
|
|
|
PipeRemoteConnected {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
connection: PipeConnection,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-20 13:57:14 +02:00
|
|
|
PipeSent {
|
|
|
|
|
message: Option<PipeMessage>,
|
|
|
|
|
listener_found: bool,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
PipeRemoteSent {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
message: Option<PipeMessage>,
|
|
|
|
|
listener_found: bool,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
|
|
|
|
PipeMessages {
|
|
|
|
|
name: String,
|
|
|
|
|
messages: Vec<PipeMessage>,
|
|
|
|
|
drained: bool,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-19 15:51:11 +02:00
|
|
|
SshProxyConnected {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
connection: Option<SshProxyConnection>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-21 01:49:48 +02:00
|
|
|
SshAdminShellOutput {
|
|
|
|
|
peer_node_id: String,
|
|
|
|
|
peer_agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
command: String,
|
|
|
|
|
output: String,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-15 15:08:20 +02:00
|
|
|
NotImplemented {
|
|
|
|
|
module: String,
|
|
|
|
|
command: String,
|
|
|
|
|
},
|
|
|
|
|
Error {
|
|
|
|
|
message: 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,
|
2026-05-16 01:54:00 +02:00
|
|
|
pub iroh_enabled: bool,
|
|
|
|
|
pub endpoint_id: Option<String>,
|
2026-05-16 03:17:45 +02:00
|
|
|
pub iroh_relay_mode: String,
|
2026-05-16 14:33:45 +02:00
|
|
|
pub iroh_local_discovery: bool,
|
2026-05-15 15:08:20 +02:00
|
|
|
pub iroh: String,
|
2026-05-22 14:33:46 +02:00
|
|
|
#[serde(default)]
|
|
|
|
|
pub native_backends: Vec<NativeBackendStatus>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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,
|
2026-05-15 15:08:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct NodeIdResponse {
|
|
|
|
|
pub agent_id: String,
|
|
|
|
|
pub node_id: String,
|
|
|
|
|
pub endpoint_id: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct KeychainStatusResponse {
|
|
|
|
|
pub initialized: bool,
|
|
|
|
|
pub admin_keys: usize,
|
2026-05-19 16:05:57 +02:00
|
|
|
pub signatures: usize,
|
2026-05-19 18:58:07 +02:00
|
|
|
pub verified_signatures: usize,
|
|
|
|
|
pub failed_signatures: usize,
|
2026-05-15 15:08:20 +02:00
|
|
|
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,
|
2026-05-16 16:36:35 +02:00
|
|
|
pub pinned: bool,
|
2026-05-15 15:08:20 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-19 15:37:02 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct CasProvider {
|
|
|
|
|
pub peer_node_id: String,
|
|
|
|
|
pub endpoint_id: String,
|
|
|
|
|
pub last_seen_ms: i64,
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 12:09:50 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
|
|
|
|
pub enum PeerControlRequest {
|
2026-05-18 17:01:50 +02:00
|
|
|
Ping {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
nonce: String,
|
|
|
|
|
},
|
|
|
|
|
AuthCheck {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
resource: String,
|
|
|
|
|
capability: String,
|
|
|
|
|
nonce: String,
|
|
|
|
|
},
|
2026-05-18 22:17:27 +02:00
|
|
|
SyncStatus {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
nonce: String,
|
|
|
|
|
},
|
2026-05-21 11:29:29 +02:00
|
|
|
KeychainSync {
|
|
|
|
|
peer_card: PeerCard,
|
2026-05-21 19:31:05 +02:00
|
|
|
since_ms: i64,
|
2026-05-21 11:29:29 +02:00
|
|
|
nonce: String,
|
|
|
|
|
},
|
2026-05-21 18:01:38 +02:00
|
|
|
AuthSync {
|
|
|
|
|
peer_card: PeerCard,
|
2026-05-21 19:31:05 +02:00
|
|
|
since_ms: i64,
|
2026-05-21 18:01:38 +02:00
|
|
|
nonce: String,
|
|
|
|
|
},
|
|
|
|
|
NodeEnrollmentSubmit {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
request: NodeEnrollmentRequest,
|
|
|
|
|
nonce: String,
|
|
|
|
|
},
|
2026-05-18 17:18:25 +02:00
|
|
|
CasFetch {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-18 17:18:25 +02:00
|
|
|
},
|
2026-05-20 13:22:40 +02:00
|
|
|
CasRootSync {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
name: String,
|
|
|
|
|
nonce: String,
|
|
|
|
|
bearer_proof: Option<BearerProof>,
|
|
|
|
|
},
|
2026-05-18 17:24:10 +02:00
|
|
|
SshCertSync {
|
|
|
|
|
peer_card: PeerCard,
|
2026-05-18 18:29:45 +02:00
|
|
|
since_ms: i64,
|
2026-05-18 17:24:10 +02:00
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-18 17:24:10 +02:00
|
|
|
},
|
|
|
|
|
SshRevocationSync {
|
|
|
|
|
peer_card: PeerCard,
|
2026-05-18 18:29:45 +02:00
|
|
|
since_ms: i64,
|
2026-05-18 17:24:10 +02:00
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-18 17:24:10 +02:00
|
|
|
},
|
2026-05-18 18:36:03 +02:00
|
|
|
KvSync {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
name: String,
|
|
|
|
|
since_ms: i64,
|
|
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-18 18:36:03 +02:00
|
|
|
},
|
2026-05-18 18:41:04 +02:00
|
|
|
PubsubPublish {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
topic: String,
|
|
|
|
|
message: String,
|
|
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-18 18:41:04 +02:00
|
|
|
},
|
2026-05-19 15:28:48 +02:00
|
|
|
PubsubSubscribe {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
topic: String,
|
|
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-19 15:28:48 +02:00
|
|
|
},
|
2026-05-18 18:45:10 +02:00
|
|
|
PipeConnect {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
target: String,
|
|
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-18 18:45:10 +02:00
|
|
|
},
|
2026-05-19 19:21:42 +02:00
|
|
|
PipeListen {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
name: String,
|
|
|
|
|
nonce: String,
|
|
|
|
|
bearer_proof: Option<BearerProof>,
|
|
|
|
|
},
|
2026-05-19 15:51:11 +02:00
|
|
|
SshProxyConnect {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-19 15:51:11 +02:00
|
|
|
},
|
2026-05-21 01:49:48 +02:00
|
|
|
SshAdminShell {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
command: String,
|
|
|
|
|
nonce: String,
|
|
|
|
|
bearer_proof: Option<BearerProof>,
|
|
|
|
|
},
|
2026-05-18 18:49:34 +02:00
|
|
|
DocumentSync {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
name: String,
|
|
|
|
|
since_ms: i64,
|
|
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-18 18:49:34 +02:00
|
|
|
},
|
2026-05-18 22:11:57 +02:00
|
|
|
DbSync {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
name: String,
|
|
|
|
|
after_db_version: Option<i64>,
|
|
|
|
|
limit: u32,
|
|
|
|
|
nonce: String,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: Option<BearerProof>,
|
2026-05-18 22:11:57 +02:00
|
|
|
},
|
2026-05-18 12:09:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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,
|
|
|
|
|
},
|
2026-05-18 17:01:50 +02:00
|
|
|
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,
|
|
|
|
|
},
|
2026-05-18 22:17:27 +02:00
|
|
|
SyncStatus {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
watermarks: Vec<SyncWatermark>,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-21 11:29:29 +02:00
|
|
|
KeychainSynced {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
ops: Vec<KeychainOp>,
|
|
|
|
|
signatures: Vec<KeychainOpSignature>,
|
2026-05-21 19:31:05 +02:00
|
|
|
high_water_ms: i64,
|
2026-05-21 11:29:29 +02:00
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-21 18:01:38 +02:00
|
|
|
AuthSynced {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
ops: Vec<AuthOp>,
|
|
|
|
|
signatures: Vec<AuthOpSignature>,
|
2026-05-21 19:31:05 +02:00
|
|
|
high_water_ms: i64,
|
2026-05-21 18:01:38 +02:00
|
|
|
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,
|
|
|
|
|
},
|
2026-05-18 17:18:25 +02:00
|
|
|
CasFetched {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
hash: BlobHash,
|
|
|
|
|
size_bytes: u64,
|
|
|
|
|
content_base64: Option<String>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-20 13:22:40 +02:00
|
|
|
CasRootSynced {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
root: Option<FileRoot>,
|
|
|
|
|
tree_content_base64: Option<String>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 17:24:10 +02:00
|
|
|
SshCertSynced {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
requests: Vec<SshCertRequest>,
|
|
|
|
|
certificates: Vec<SshCertificateRecord>,
|
2026-05-22 16:51:30 +02:00
|
|
|
log_entries: Vec<SshDistributionLogEntry>,
|
2026-05-18 18:29:45 +02:00
|
|
|
high_water_ms: i64,
|
2026-05-18 17:24:10 +02:00
|
|
|
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<SshRevocationEntry>,
|
2026-05-22 16:51:30 +02:00
|
|
|
log_entries: Vec<SshDistributionLogEntry>,
|
2026-05-18 18:29:45 +02:00
|
|
|
high_water_ms: i64,
|
2026-05-18 17:24:10 +02:00
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 18:36:03 +02:00
|
|
|
KvSynced {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
entries: Vec<KvSyncEntry>,
|
|
|
|
|
high_water_ms: i64,
|
2026-05-22 16:24:11 +02:00
|
|
|
docs_ticket: Option<String>,
|
2026-05-18 18:36:03 +02:00
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 18:41:04 +02:00
|
|
|
PubsubPublished {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
message: Option<PubsubMessage>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-19 15:28:48 +02:00
|
|
|
PubsubSubscribed {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
topic: String,
|
|
|
|
|
messages: Vec<PubsubMessage>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 18:45:10 +02:00
|
|
|
PipeConnected {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
connection: Option<PipeConnection>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-19 19:21:42 +02:00
|
|
|
PipeListening {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
listener: Option<PipeListener>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-19 15:51:11 +02:00
|
|
|
SshProxyConnected {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
connection: Option<SshProxyConnection>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-21 01:49:48 +02:00
|
|
|
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,
|
|
|
|
|
},
|
2026-05-18 18:49:34 +02:00
|
|
|
DocumentSynced {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
state: Option<DocumentState>,
|
|
|
|
|
high_water_ms: i64,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 22:11:57 +02:00
|
|
|
DbSynced {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
name: String,
|
|
|
|
|
batch: Option<CrSqliteChangeBatch>,
|
|
|
|
|
high_water_db_version: Option<i64>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-18 12:09:50 +02:00
|
|
|
Error {
|
|
|
|
|
message: String,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-20 13:57:14 +02:00
|
|
|
#[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<BearerProof>,
|
|
|
|
|
},
|
2026-05-21 01:12:01 +02:00
|
|
|
TcpConnect {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
target_addr: String,
|
|
|
|
|
nonce: String,
|
|
|
|
|
bearer_proof: Option<BearerProof>,
|
|
|
|
|
},
|
2026-05-21 01:15:51 +02:00
|
|
|
UnixConnect {
|
|
|
|
|
peer_card: PeerCard,
|
|
|
|
|
target_path: PathBuf,
|
|
|
|
|
nonce: String,
|
|
|
|
|
bearer_proof: Option<BearerProof>,
|
|
|
|
|
},
|
2026-05-20 13:57:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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<Option<PipeMessage>>,
|
|
|
|
|
listener_found: bool,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-21 01:12:01 +02:00
|
|
|
Connected {
|
|
|
|
|
node_id: String,
|
|
|
|
|
agent_id: String,
|
|
|
|
|
endpoint_id: String,
|
|
|
|
|
remote_endpoint_id: String,
|
|
|
|
|
connection: Option<PipeConnection>,
|
|
|
|
|
allowed: bool,
|
|
|
|
|
reason: String,
|
|
|
|
|
evaluated_ops: usize,
|
|
|
|
|
nonce: String,
|
|
|
|
|
note: String,
|
|
|
|
|
},
|
2026-05-20 13:57:14 +02:00
|
|
|
Error {
|
|
|
|
|
message: String,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 22:17:27 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct SyncWatermark {
|
|
|
|
|
pub stream: String,
|
|
|
|
|
pub high_water: i64,
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-21 19:31:05 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct SyncPeerStatus {
|
|
|
|
|
pub peer_node_id: String,
|
|
|
|
|
pub streams: Vec<SyncStreamStatus>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct SyncStreamStatus {
|
|
|
|
|
pub stream: String,
|
|
|
|
|
pub cursor_ms: i64,
|
2026-05-22 15:00:09 +02:00
|
|
|
pub state: String,
|
|
|
|
|
pub stale: bool,
|
|
|
|
|
pub stale_after_ms: i64,
|
2026-05-21 19:31:05 +02:00
|
|
|
pub last_attempt_ms: Option<i64>,
|
|
|
|
|
pub last_success_ms: Option<i64>,
|
|
|
|
|
pub last_error: Option<String>,
|
|
|
|
|
pub last_imported: usize,
|
|
|
|
|
pub last_rejected: usize,
|
2026-05-22 15:00:09 +02:00
|
|
|
pub next_action: String,
|
2026-05-21 19:31:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct SyncPeerRun {
|
|
|
|
|
pub peer_node_id: String,
|
|
|
|
|
pub streams: Vec<SyncStreamRun>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[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<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
pub enum ControlError {
|
|
|
|
|
#[error("json error: {0}")]
|
|
|
|
|
Json(#[from] serde_json::Error),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn encode_request(request: &ControlRequest) -> Result<String, ControlError> {
|
|
|
|
|
let mut line = serde_json::to_string(request)?;
|
|
|
|
|
line.push('\n');
|
|
|
|
|
Ok(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn decode_request(line: &str) -> Result<ControlRequest, ControlError> {
|
|
|
|
|
serde_json::from_str(line).map_err(ControlError::from)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn encode_response(response: &ControlResponse) -> Result<String, ControlError> {
|
|
|
|
|
let mut line = serde_json::to_string(response)?;
|
|
|
|
|
line.push('\n');
|
|
|
|
|
Ok(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn decode_response(line: &str) -> Result<ControlResponse, ControlError> {
|
|
|
|
|
serde_json::from_str(line).map_err(ControlError::from)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 12:09:50 +02:00
|
|
|
pub fn encode_peer_request(request: &PeerControlRequest) -> Result<String, ControlError> {
|
|
|
|
|
let mut line = serde_json::to_string(request)?;
|
|
|
|
|
line.push('\n');
|
|
|
|
|
Ok(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn decode_peer_request(line: &str) -> Result<PeerControlRequest, ControlError> {
|
|
|
|
|
serde_json::from_str(line).map_err(ControlError::from)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn encode_peer_response(response: &PeerControlResponse) -> Result<String, ControlError> {
|
|
|
|
|
let mut line = serde_json::to_string(response)?;
|
|
|
|
|
line.push('\n');
|
|
|
|
|
Ok(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn decode_peer_response(line: &str) -> Result<PeerControlResponse, ControlError> {
|
|
|
|
|
serde_json::from_str(line).map_err(ControlError::from)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-20 13:57:14 +02:00
|
|
|
pub fn encode_pipe_wire_request(request: &PipeWireRequest) -> Result<String, ControlError> {
|
|
|
|
|
let mut line = serde_json::to_string(request)?;
|
|
|
|
|
line.push('\n');
|
|
|
|
|
Ok(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn decode_pipe_wire_request(line: &str) -> Result<PipeWireRequest, ControlError> {
|
|
|
|
|
serde_json::from_str(line).map_err(ControlError::from)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn encode_pipe_wire_response(response: &PipeWireResponse) -> Result<String, ControlError> {
|
|
|
|
|
let mut line = serde_json::to_string(response)?;
|
|
|
|
|
line.push('\n');
|
|
|
|
|
Ok(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn decode_pipe_wire_response(line: &str) -> Result<PipeWireResponse, ControlError> {
|
|
|
|
|
serde_json::from_str(line).map_err(ControlError::from)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn control_request_response_serialization_roundtrip() {
|
2026-05-19 16:04:20 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 11:29:29 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 18:01:38 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 11:29:29 +02:00
|
|
|
let request = ControlRequest::KeychainSync {
|
|
|
|
|
node: "work-laptop".to_owned(),
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
let request = ControlRequest::CasHas {
|
|
|
|
|
hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(),
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-23 01:17:30 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-15 15:08:20 +02:00
|
|
|
let response = ControlResponse::CasHas {
|
|
|
|
|
hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(),
|
|
|
|
|
present: true,
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_response(&encode_response(&response).expect("encode")).expect("decode"),
|
|
|
|
|
response
|
|
|
|
|
);
|
2026-05-17 18:29:47 +02:00
|
|
|
|
2026-05-23 01:17:30 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 11:29:29 +02:00
|
|
|
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,
|
2026-05-21 19:31:05 +02:00
|
|
|
high_water_ms: 42,
|
2026-05-21 11:29:29 +02:00
|
|
|
note: "trusted admin signatures only".to_owned(),
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_response(&encode_response(&response).expect("encode")).expect("decode"),
|
|
|
|
|
response
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 18:01:38 +02:00
|
|
|
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,
|
2026-05-21 19:31:05 +02:00
|
|
|
high_water_ms: 42,
|
2026-05-21 18:01:38 +02:00
|
|
|
note: "trusted admin signatures only".to_owned(),
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_response(&encode_response(&response).expect("encode")).expect("decode"),
|
|
|
|
|
response
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 19:31:05 +02:00
|
|
|
let response = ControlResponse::SyncStatus {
|
|
|
|
|
peers: vec![SyncPeerStatus {
|
|
|
|
|
peer_node_id: "node:peer".to_owned(),
|
|
|
|
|
streams: vec![SyncStreamStatus {
|
|
|
|
|
stream: "keychain".to_owned(),
|
|
|
|
|
cursor_ms: 42,
|
2026-05-22 15:00:09 +02:00
|
|
|
state: "ok".to_owned(),
|
|
|
|
|
stale: false,
|
|
|
|
|
stale_after_ms: 120_000,
|
2026-05-21 19:31:05 +02:00
|
|
|
last_attempt_ms: Some(43),
|
|
|
|
|
last_success_ms: Some(43),
|
|
|
|
|
last_error: None,
|
|
|
|
|
last_imported: 2,
|
|
|
|
|
last_rejected: 0,
|
2026-05-22 15:00:09 +02:00
|
|
|
next_action: "none".to_owned(),
|
2026-05-21 19:31:05 +02:00
|
|
|
}],
|
|
|
|
|
}],
|
|
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 19:08:08 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 17:18:25 +02:00
|
|
|
let request = ControlRequest::CasFetch {
|
|
|
|
|
node: "node:peer".to_owned(),
|
|
|
|
|
hash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".into(),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: Some("bearer:test".to_owned()),
|
2026-05-18 17:18:25 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 15:37:02 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-17 18:29:47 +02:00
|
|
|
let request = ControlRequest::SshRevocationExport {
|
|
|
|
|
out: PathBuf::from("revocations.krl-spec"),
|
|
|
|
|
format: "openssh-krl-spec".to_owned(),
|
2026-05-18 11:51:12 +02:00
|
|
|
ca_public: None,
|
2026-05-19 15:44:13 +02:00
|
|
|
subject: None,
|
2026-05-17 18:29:47 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 11:56:42 +02:00
|
|
|
let request = ControlRequest::SshRevocationImport {
|
|
|
|
|
path: PathBuf::from("revocations.jsonl"),
|
|
|
|
|
format: "jsonl".to_owned(),
|
2026-05-19 15:44:13 +02:00
|
|
|
subject: None,
|
2026-05-18 11:56:42 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 17:24:10 +02:00
|
|
|
let request = ControlRequest::SshCertSync {
|
|
|
|
|
node: "node:ca".to_owned(),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: None,
|
2026-05-18 17:24:10 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let request = ControlRequest::SshRevocationSync {
|
|
|
|
|
node: "node:ca".to_owned(),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: None,
|
2026-05-18 17:24:10 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-17 18:29:47 +02:00
|
|
|
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
|
|
|
|
|
);
|
2026-05-17 19:59:03 +02:00
|
|
|
|
2026-05-18 17:24:10 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 18:36:03 +02:00
|
|
|
let request = ControlRequest::KvSync {
|
|
|
|
|
node: "node:peer".to_owned(),
|
|
|
|
|
name: "prefs".to_owned(),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: None,
|
2026-05-18 18:36:03 +02:00
|
|
|
};
|
|
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-17 19:59:03 +02:00
|
|
|
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
|
|
|
|
|
);
|
2026-05-17 20:17:26 +02:00
|
|
|
|
|
|
|
|
let request = ControlRequest::PipeListen {
|
|
|
|
|
name: "inbox".to_owned(),
|
2026-05-19 19:21:42 +02:00
|
|
|
node: Some("node:peer".to_owned()),
|
|
|
|
|
bearer_secret: Some("bearer:test".to_owned()),
|
2026-05-17 20:17:26 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
2026-05-17 20:32:36 +02:00
|
|
|
|
2026-05-18 18:45:10 +02:00
|
|
|
let request = ControlRequest::PipeConnect {
|
|
|
|
|
target: "inbox".to_owned(),
|
|
|
|
|
node: Some("node:peer".to_owned()),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: None,
|
2026-05-18 18:45:10 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-20 13:57:14 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 18:41:04 +02:00
|
|
|
let request = ControlRequest::PubsubPub {
|
|
|
|
|
topic: "presence/test".to_owned(),
|
|
|
|
|
message: "online".to_owned(),
|
|
|
|
|
node: Some("node:peer".to_owned()),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: None,
|
2026-05-18 18:41:04 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 15:28:48 +02:00
|
|
|
let request = ControlRequest::PubsubSub {
|
|
|
|
|
topic: "presence/test".to_owned(),
|
|
|
|
|
node: Some("node:peer".to_owned()),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: None,
|
2026-05-19 15:28:48 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 18:41:04 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 15:28:48 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 18:45:10 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 19:21:42 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 15:51:11 +02:00
|
|
|
let request = ControlRequest::SshProxyConnect {
|
|
|
|
|
node: "node:peer".to_owned(),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: None,
|
2026-05-19 15:51:11 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 01:03:38 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 01:49:48 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 15:51:11 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 01:49:48 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-17 20:32:36 +02:00
|
|
|
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
|
|
|
|
|
);
|
2026-05-18 03:50:09 +02:00
|
|
|
|
2026-05-18 22:11:57 +02:00
|
|
|
let request = ControlRequest::DbSync {
|
|
|
|
|
node: "node:peer".to_owned(),
|
|
|
|
|
name: "notes".to_owned(),
|
|
|
|
|
limit: 50,
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_secret: None,
|
2026-05-18 22:11:57 +02:00
|
|
|
};
|
|
|
|
|
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,
|
2026-05-19 19:02:39 +02:00
|
|
|
changes_applied: 1,
|
2026-05-18 22:11:57 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 03:50:09 +02:00
|
|
|
let request = ControlRequest::CasRootScan {
|
|
|
|
|
name: "notes".to_owned(),
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
2026-05-18 03:57:26 +02:00
|
|
|
|
2026-05-20 13:22:40 +02:00
|
|
|
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,
|
2026-05-21 01:40:50 +02:00
|
|
|
sync_conflicts: Vec::new(),
|
2026-05-20 13:22:40 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-20 13:30:57 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 03:57:26 +02:00
|
|
|
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
|
|
|
|
|
);
|
2026-05-18 04:03:52 +02:00
|
|
|
|
|
|
|
|
let request = ControlRequest::PeerCardExport { out: None };
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
2026-05-18 12:09:50 +02:00
|
|
|
|
|
|
|
|
let request = ControlRequest::PeerPing {
|
|
|
|
|
node: "node:peer".to_owned(),
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_request(&encode_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 17:01:50 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 12:09:50 +02:00
|
|
|
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
|
|
|
|
|
);
|
2026-05-18 17:01:50 +02:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
);
|
2026-05-18 17:18:25 +02:00
|
|
|
|
2026-05-18 22:17:27 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 17:18:25 +02:00
|
|
|
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
|
|
|
|
|
);
|
2026-05-18 17:24:10 +02:00
|
|
|
|
2026-05-20 13:22:40 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 22:11:57 +02:00
|
|
|
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(),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: None,
|
2026-05-18 22:11:57 +02:00
|
|
|
};
|
|
|
|
|
assert_eq!(
|
2026-05-18 22:17:27 +02:00
|
|
|
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!(
|
2026-05-18 22:11:57 +02:00
|
|
|
decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-18 17:24:10 +02:00
|
|
|
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(),
|
2026-05-22 16:51:30 +02:00
|
|
|
log_entries: Vec::new(),
|
2026-05-18 18:29:45 +02:00
|
|
|
high_water_ms: 42,
|
2026-05-18 17:24:10 +02:00
|
|
|
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(),
|
2026-05-22 16:51:30 +02:00
|
|
|
log_entries: Vec::new(),
|
2026-05-18 18:29:45 +02:00
|
|
|
high_water_ms: 42,
|
2026-05-18 17:24:10 +02:00
|
|
|
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
|
|
|
|
|
);
|
2026-05-18 18:36:03 +02:00
|
|
|
|
|
|
|
|
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,
|
2026-05-22 16:24:11 +02:00
|
|
|
docs_ticket: Some("doc-ticket".to_owned()),
|
2026-05-18 18:36:03 +02:00
|
|
|
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
|
|
|
|
|
);
|
2026-05-18 18:41:04 +02:00
|
|
|
|
|
|
|
|
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(),
|
2026-05-19 15:28:48 +02:00
|
|
|
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(),
|
2026-05-19 19:16:30 +02:00
|
|
|
bearer_proof: None,
|
2026-05-19 15:28:48 +02:00
|
|
|
};
|
|
|
|
|
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(),
|
2026-05-18 18:41:04 +02:00
|
|
|
note: "lossy".to_owned(),
|
|
|
|
|
};
|
|
|
|
|
assert_eq!(
|
|
|
|
|
decode_peer_response(&encode_peer_response(&response).expect("encode"))
|
|
|
|
|
.expect("decode"),
|
|
|
|
|
response
|
|
|
|
|
);
|
2026-05-18 18:45:10 +02:00
|
|
|
|
|
|
|
|
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"))
|
2026-05-19 15:51:11 +02:00
|
|
|
.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(),
|
2026-05-19 19:16:30 +02:00
|
|
|
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(),
|
|
|
|
|
}),
|
2026-05-19 15:51:11 +02:00
|
|
|
};
|
2026-05-19 19:21:42 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 01:49:48 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 19:21:42 +02:00
|
|
|
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,
|
|
|
|
|
};
|
2026-05-19 15:51:11 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
decode_peer_request(&encode_peer_request(&request).expect("encode")).expect("decode"),
|
|
|
|
|
request
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-20 13:57:14 +02:00
|
|
|
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"))
|
2026-05-21 01:12:01 +02:00
|
|
|
.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"))
|
2026-05-20 13:57:14 +02:00
|
|
|
.expect("decode"),
|
|
|
|
|
response
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-21 01:15:51 +02:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
|
2026-05-19 15:51:11 +02:00
|
|
|
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"))
|
2026-05-21 01:49:48 +02:00
|
|
|
.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"))
|
2026-05-18 18:45:10 +02:00
|
|
|
.expect("decode"),
|
|
|
|
|
response
|
|
|
|
|
);
|
2026-05-18 22:11:57 +02:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
);
|
2026-05-15 15:08:20 +02:00
|
|
|
}
|
|
|
|
|
}
|