Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2a61f08
add said-bridge crate with off-chain register and lookup
achillewasque Jun 8, 2026
d1c7036
said: anchor pipeline with sqlite cursor and fixture mode
achillewasque Jun 8, 2026
d19b811
said: xchain inbox poll + free-tier check + outbound send
achillewasque Jun 8, 2026
18bb2e4
said: register-on-chain, verify, validate-work, sponsor instructions
achillewasque Jun 8, 2026
5ad2943
said-bridge: TS worker package
iko-rane Jun 8, 2026
af5df17
said: free-tier schema matches live api.saidprotocol.com
mizuki0x Jun 8, 2026
c71db2c
said: drop sponsor_register / sponsor_verify surface
nr00x Jun 8, 2026
0310dd3
said: trim docstrings, fix lookup schema, drop dead off-chain register
nr00x Jun 8, 2026
4df19b2
docs: realign ipc-and-http-gateway line refs after said-bridge additions
achillewasque Jun 8, 2026
8978b55
style: rustfmt and clippy-clean covenant-said-bridge
achillewasque Jun 8, 2026
734348d
test: cover said-bridge worker json protocol end-to-end
achillewasque Jun 8, 2026
6a211c6
test: cover said-bridge anchor live worker path
achillewasque Jun 8, 2026
6030ec5
test: cover said-bridge rest envelope mapping end-to-end
achillewasque Jun 8, 2026
e3f331b
test: cover said-bridge paid-op input validation arms
achillewasque Jun 8, 2026
51443ad
test: cover said-bridge worker decode failure arms
achillewasque Jun 8, 2026
bb8cf2d
test: lock said-bridge get_verified and validate_work paid gates
achillewasque Jun 8, 2026
c28f7dd
test: cover covenantd said dispatch guard and status arms
achillewasque Jun 8, 2026
6e184b8
test: cover covenantd said anchor fixture-mode dispatch
achillewasque Jun 8, 2026
c30358d
test: cover covenantd said rest dispatch mapping
achillewasque Jun 8, 2026
1140d2d
test: cover covenantd said dispatch error-mapping arms
achillewasque Jun 8, 2026
a62d58e
test: cover covenantd said paid-success dispatch mapping
achillewasque Jun 8, 2026
9ec2830
test: cover said-bridge agent-card lookup deserialization defaults
covenant Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Covenant sits below agent applications and above the host operating system. It owns the state, authority, and accountability concerns that recur across agent frameworks — scoped capabilities, durable memory, runtime isolation, append-only audit, and commit-scoped provenance — so individual frameworks can stop reinventing them.

<!-- METRICS:START -->
**Status.** Local control plane is real and live-tested (24 Rust crates, ~193k lines, 2537 source-discovered Rust tests including 387 live boundary tests). Production-grade sandboxing for hostile agent code, networked multi-peer operation, and daemon-driven on-chain settlement are roadmap. See [BUILT.md](./BUILT.md) for the explicit honesty boundary.
**Status.** Local control plane is real and live-tested (25 Rust crates, ~197k lines, 2605 source-discovered Rust tests including 387 live boundary tests). Production-grade sandboxing for hostile agent code, networked multi-peer operation, and daemon-driven on-chain settlement are roadmap. See [BUILT.md](./BUILT.md) for the explicit honesty boundary.
<!-- METRICS:END -->

- **Web:** [opencovenant.org](https://opencovenant.org)
Expand Down
24 changes: 24 additions & 0 deletions agent-os/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions agent-os/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ members = [
"crates/covenant-x402",
"crates/covenant-hyre",
"crates/covenant-sap-bridge",
"crates/covenant-said-bridge",
"crates/covenant-stake-keeper",
"crates/covenantd",
"crates/covenant",
Expand Down
2 changes: 1 addition & 1 deletion agent-os/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ The Linux host, `runsc`, rootfs, and CI adoption contract is maintained internal
| State and tools | `covenant-memory`, `covenant-tools`, `covenant-llm`, `covenant-mcp`, `covenant-a2a` |
| Compositor | `covenant-tui` |
| Payments | `covenant-x402`, `covenant-hyre` |
| Settlement | `covenant-settlement`, `covenant-stake-keeper`, `covenant-sap-bridge`, `programs/settlement`, `programs/stake` |
| Settlement | `covenant-settlement`, `covenant-stake-keeper`, `covenant-sap-bridge`, `covenant-said-bridge`, `programs/settlement`, `programs/stake` |

## Operating Model

Expand Down
131 changes: 131 additions & 0 deletions agent-os/crates/covenant-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,45 @@ pub enum Request {
#[serde(default)]
expires_at_unix: Option<u64>,
},
SaidStatus,
SaidLookup {
wallet: String,
},
SaidAnchor {
start_audit_index: u64,
end_audit_index: u64,
merkle_root_hex: String,
#[serde(default)]
live: bool,
},
SaidAnchorStatus {
#[serde(default = "default_recent_limit")]
recent_limit: usize,
},
SaidInbox {
chain: String,
address: String,
},
SaidFreeTier {
address: String,
},
SaidSend {
source_chain: String,
source_address: String,
target_chain: String,
target_address: String,
payload_json: String,
},
SaidRegisterOnChain {
metadata_uri: String,
},
SaidGetVerified,
SaidValidateWork {
agent: String,
task_hash_hex: String,
passed: bool,
evidence_uri: String,
},
}

fn default_recent_limit() -> usize {
Expand Down Expand Up @@ -1016,11 +1055,103 @@ pub enum Response {
agent_pda: String,
signature: String,
},
SaidStatus {
enabled: bool,
cluster: String,
program_id: String,
rpc_url: String,
api_base_url: String,
paid_gates: String,
has_signer: bool,
},
SaidAgent {
wallet: String,
pda: Option<String>,
owner: Option<String>,
name: Option<String>,
description: Option<String>,
metadata_uri: Option<String>,
is_verified: bool,
sponsored: bool,
reputation_score: f64,
feedback_count: u64,
activity_count: u64,
registered_at: Option<String>,
},
SaidAnchored {
anchor_index: u64,
start_seq: u64,
end_seq: u64,
merkle_root_hex: String,
tx_sig: String,
slot: u64,
fixture: bool,
},
SaidAnchorStatus {
next_index: u64,
last_confirmed_index: Option<u64>,
pending: u64,
recent: Vec<SaidAnchorRow>,
},
SaidInbox {
chain: String,
address: String,
messages: Vec<SaidInboxMessage>,
},
SaidFreeTier {
address: String,
used: u32,
remaining: u32,
limit: u32,
paid_price: Option<String>,
payment_chains: Vec<String>,
},
SaidSent {
message_id: String,
free_tier_remaining: Option<u32>,
delivered_at: Option<i64>,
},
SaidOnChainRegistered {
agent_pda: String,
owner: String,
signature: String,
},
SaidVerified {
signature: String,
slot: u64,
},
SaidValidationPosted {
validation_pda: String,
validator: String,
signature: String,
},
Error {
message: String,
},
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SaidInboxMessage {
pub id: String,
pub source_chain: String,
pub source_address: String,
pub target_chain: String,
pub target_address: String,
pub payload: Option<serde_json::Value>,
pub created_at: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SaidAnchorRow {
pub anchor_index: u64,
pub start_seq: u64,
pub end_seq: u64,
pub merkle_root_hex: String,
pub tx_sig: String,
pub slot: u64,
pub submitted_at_ms: i64,
}

#[derive(Debug, thiserror::Error)]
pub enum IpcError {
#[error("io: {0}")]
Expand Down
23 changes: 23 additions & 0 deletions agent-os/crates/covenant-said-bridge/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "covenant-said-bridge"
version = "0.0.0"
edition = "2021"
license = "Apache-2.0"
authors = ["Covenant contributors"]
repository = "https://github.com/open-covenant/covenant"
description = "Opt-in bridge between Covenant and SAID Protocol (Solana Agent Identity Standard)."

[dependencies]
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
tokio = { workspace = true }
reqwest = { workspace = true }
rusqlite = { workspace = true }
parking_lot = { workspace = true }
hex = "0.4"
url = "2"

[dev-dependencies]
tempfile = { workspace = true }
90 changes: 90 additions & 0 deletions agent-os/crates/covenant-said-bridge/src/agent_card.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! Public-API reads against `api.saidprotocol.com`. Lookup hits
//! `GET /api/agents/:wallet`; SAID indexes agents from on-chain
//! `register_agent`, so there is no off-chain register call.

use serde::{Deserialize, Serialize};

use crate::client::SaidBridge;
use crate::rest;
use crate::Result;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentLookup {
pub wallet: String,
#[serde(default)]
pub pda: Option<String>,
#[serde(default)]
pub owner: Option<String>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub metadata_uri: Option<String>,
#[serde(default)]
pub is_verified: bool,
#[serde(default)]
pub sponsored: bool,
#[serde(default)]
pub reputation_score: f64,
#[serde(default)]
pub feedback_count: u64,
#[serde(default)]
pub activity_count: u64,
#[serde(default)]
pub registered_at: Option<String>,
}

impl SaidBridge {
pub async fn lookup(&self, wallet: &str) -> Result<AgentLookup> {
self.require_enabled()?;
let client = rest::build_client(self.config().rest_timeout)?;
let path = format!("/api/agents/{wallet}");
rest::get_json(&client, &self.config().api_base_url, &path).await
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn lookup_defaults_sparse_record() {
// SAID agent records are sparse; only `wallet` is guaranteed, so every
// other field must fall back to its default instead of failing decode.
let agent: AgentLookup =
serde_json::from_value(serde_json::json!({ "wallet": "Wal111" })).unwrap();
assert_eq!(agent.wallet, "Wal111");
assert!(agent.pda.is_none());
assert!(agent.metadata_uri.is_none());
assert!(agent.registered_at.is_none());
assert!(!agent.is_verified);
assert!(!agent.sponsored);
assert!(agent.reputation_score.abs() < f64::EPSILON);
assert_eq!(agent.feedback_count, 0);
assert_eq!(agent.activity_count, 0);
}

#[test]
fn lookup_maps_camel_case_keys() {
let agent: AgentLookup = serde_json::from_value(serde_json::json!({
"wallet": "Wal111",
"metadataUri": "ipfs://meta",
"isVerified": true,
"sponsored": true,
"reputationScore": 4.5,
"feedbackCount": 12,
"activityCount": 99,
"registeredAt": "2026-01-02T03:04:05Z"
}))
.unwrap();
assert_eq!(agent.metadata_uri.as_deref(), Some("ipfs://meta"));
assert_eq!(agent.registered_at.as_deref(), Some("2026-01-02T03:04:05Z"));
assert!(agent.is_verified);
assert!(agent.sponsored);
assert_eq!(agent.feedback_count, 12);
assert_eq!(agent.activity_count, 99);
assert!((agent.reputation_score - 4.5).abs() < f64::EPSILON);
}
}
Loading
Loading