Skip to repository content223 lines · 7.8 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:16:40.487Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
omega_identity.rs
1//! Operator CLI for Omega Nostr identity custody.
2//!
3//! Ready-state UI only offers Protect, not Reset. Use this tool to inspect
4//! and clear channel custody through the typed IdentityService path.
5
6use std::{path::PathBuf, process::ExitCode, str::FromStr};
7
8use anyhow::{Context as _, Result, bail};
9use app_identity::AppChannel;
10use clap::{Parser, Subcommand};
11use omega_identity::{
12 CustodyState, IdentityRef, IdentityService, NostrPublicKeyHex, OwnerAttestationRequest,
13 ReceiptRef,
14};
15use serde_json::json;
16
17#[derive(Parser, Debug)]
18#[command(
19 name = "omega-identity",
20 about = "Inspect and reset Omega Nostr identity custody"
21)]
22struct Args {
23 /// Release channel that owns the identity (`dev`, `nightly`, `rc`, `stable`).
24 #[arg(long, default_value = "rc")]
25 channel: String,
26
27 /// Optional override for the channel data root (parent of `identity/`).
28 #[arg(long)]
29 data_root: Option<PathBuf>,
30
31 #[command(subcommand)]
32 command: Command,
33}
34
35#[derive(Subcommand, Debug)]
36enum Command {
37 /// Print custody state and public identity refs as JSON.
38 Status,
39 /// Sign a public NIP-OA auth tag for an admitted agent without exporting the owner key.
40 AttestAgent {
41 #[arg(long)]
42 agent_public_key_hex: String,
43 #[arg(long, default_value = "")]
44 conditions: String,
45 #[arg(long)]
46 request: Option<String>,
47 },
48 /// Record a marker-first reset (relaunch-required). Requires --yes.
49 Reset {
50 /// Confirm destructive reset of the current Ready identity.
51 #[arg(long)]
52 yes: bool,
53 /// Optional authorization receipt; generated when omitted.
54 #[arg(long)]
55 receipt: Option<String>,
56 /// Optional expected identity_ref; defaults to the inspected Ready identity.
57 #[arg(long)]
58 identity_ref: Option<String>,
59 },
60 /// Resume a pending or failed reset (deletes Keychain + public documents).
61 Resume,
62 /// Acknowledge a completed reset after relaunch proof (Absent).
63 Acknowledge,
64 /// Reset, resume, and acknowledge in one operator flow. Requires --yes.
65 Wipe {
66 #[arg(long)]
67 yes: bool,
68 #[arg(long)]
69 receipt: Option<String>,
70 },
71}
72
73fn main() -> ExitCode {
74 match run() {
75 Ok(()) => ExitCode::SUCCESS,
76 Err(error) => {
77 eprintln!("error: {error:#}");
78 ExitCode::FAILURE
79 }
80 }
81}
82
83fn run() -> Result<()> {
84 let args = Args::parse();
85 let channel = AppChannel::from_str(&args.channel).map_err(|_| {
86 anyhow::anyhow!(
87 "invalid --channel {}; expected dev|nightly|rc|stable",
88 args.channel
89 )
90 })?;
91 let service = match args.data_root {
92 Some(root) => IdentityService::for_channel_data_root(channel, root),
93 None => IdentityService::for_channel(channel),
94 };
95
96 match args.command {
97 Command::Status => {
98 let inspection = service
99 .inspect_details()
100 .context("inspect identity custody")?;
101 println!("{}", serde_json::to_string_pretty(&inspection)?);
102 }
103 Command::AttestAgent {
104 agent_public_key_hex,
105 conditions,
106 request,
107 } => {
108 let inspection = service.inspect().context("inspect identity custody")?;
109 let identity = inspection
110 .identity
111 .context("no Ready identity to attest an agent")?;
112 let stamp = std::time::SystemTime::now()
113 .duration_since(std::time::UNIX_EPOCH)
114 .context("system clock")?
115 .as_millis();
116 let result = service
117 .sign_owner_attestation(&OwnerAttestationRequest {
118 request_ref: ReceiptRef::new(request.unwrap_or_else(|| {
119 format!("omega-identity-cli-agent-attestation-{stamp}")
120 }))?,
121 identity_ref: identity.identity_ref().clone(),
122 agent_public_key_hex: NostrPublicKeyHex::new(agent_public_key_hex)?,
123 conditions,
124 })
125 .context("sign owner agent attestation")?;
126 println!("{}", serde_json::to_string_pretty(&result)?);
127 }
128 Command::Reset {
129 yes,
130 receipt,
131 identity_ref,
132 } => {
133 if !yes {
134 bail!("refusing reset without --yes");
135 }
136 let result = reset_identity(&service, receipt, identity_ref)?;
137 println!("{}", serde_json::to_string_pretty(&result)?);
138 }
139 Command::Resume => {
140 let result = service
141 .resume_pending_reset()
142 .context("resume pending identity reset")?;
143 println!("{}", serde_json::to_string_pretty(&result)?);
144 }
145 Command::Acknowledge => {
146 let result = service
147 .acknowledge_relaunch()
148 .context("acknowledge completed identity reset")?;
149 println!("{}", serde_json::to_string_pretty(&result)?);
150 }
151 Command::Wipe { yes, receipt } => {
152 if !yes {
153 bail!("refusing wipe without --yes");
154 }
155 let reset = reset_identity(&service, receipt, None)?;
156 let resumed = match service.resume_pending_reset() {
157 Ok(result) => result,
158 Err(_) => {
159 // inspect() also resumes a pending marker
160 service.inspect().context("inspect after reset marker")?
161 }
162 };
163 let acknowledged = if resumed.state == CustodyState::RelaunchRequired {
164 service
165 .acknowledge_relaunch()
166 .context("acknowledge completed identity reset")?
167 } else if resumed.state == CustodyState::Absent {
168 resumed.clone()
169 } else {
170 bail!(
171 "reset did not reach relaunch-required or absent; state={:?}",
172 resumed.state
173 );
174 };
175 println!(
176 "{}",
177 serde_json::to_string_pretty(&json!({
178 "channel": channel.storage_slug(),
179 "reset": reset,
180 "resumed": resumed,
181 "acknowledged": acknowledged,
182 }))?
183 );
184 }
185 }
186 Ok(())
187}
188
189fn reset_identity(
190 service: &IdentityService,
191 receipt: Option<String>,
192 identity_ref: Option<String>,
193) -> Result<omega_identity::CustodyResult> {
194 let inspection = service
195 .inspect_details()
196 .context("inspect identity before reset")?;
197 let expected = match identity_ref {
198 Some(value) => IdentityRef::new(value).context("parse --identity-ref")?,
199 None => {
200 let identity = inspection
201 .custody
202 .identity
203 .as_ref()
204 .context("no Ready identity to reset; run status first")?;
205 IdentityRef::new(identity.identity_ref().as_str()).context("copy identity_ref")?
206 }
207 };
208 let authorization = match receipt {
209 Some(value) => ReceiptRef::new(value).context("parse --receipt")?,
210 None => {
211 let stamp = std::time::SystemTime::now()
212 .duration_since(std::time::UNIX_EPOCH)
213 .context("system clock")?
214 .as_millis();
215 ReceiptRef::new(format!("omega-identity-cli-reset-{stamp}"))
216 .context("build reset receipt")?
217 }
218 };
219 service
220 .reset(&expected, authorization)
221 .context("record identity reset marker")
222}
223