Skip to repository content365 lines · 11.8 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:24:06.145Z 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_proof.rs
1use std::{io::Read as _, path::PathBuf, process::ExitCode};
2
3use anyhow::{Context as _, Result, bail};
4use clap::{Parser, Subcommand, ValueEnum};
5use omega_identity::{
6 AdmittedSigningRequest, IDENTITY_PROOF_KEYRING_ACCOUNT, IDENTITY_PROOF_KEYRING_SERVICE,
7 IDENTITY_PROOF_PROTOCOL, IdentityProofService, IdentityRef, ProofCrashBoundary,
8 ProofSafeScenario, ReceiptRef, RecoveryPassword, SigningPurpose, UnsignedEventTemplate,
9};
10use serde::Serialize;
11use serde_json::json;
12
13#[derive(Parser, Debug)]
14#[command(
15 name = "omega-identity-proof",
16 about = "Run explicit disposable Omega identity custody proofs"
17)]
18struct Args {
19 #[arg(long)]
20 root: PathBuf,
21
22 #[command(subcommand)]
23 command: Command,
24}
25
26#[derive(Subcommand, Debug)]
27enum Command {
28 Initialize {
29 #[arg(long)]
30 confirm_disposable: bool,
31 },
32 Create {
33 #[arg(long)]
34 receipt: String,
35 #[arg(long, value_enum)]
36 crash_after: Option<CrashBoundary>,
37 },
38 ResumeCreate,
39 Inspect,
40 ProcessStart {
41 #[arg(long, value_enum)]
42 crash_after: Option<CrashBoundary>,
43 },
44 Sign {
45 #[arg(long)]
46 identity_ref: String,
47 #[arg(long)]
48 request: String,
49 },
50 ProbeForged {
51 #[arg(long)]
52 request: String,
53 },
54 ProbeStale {
55 #[arg(long)]
56 stale_identity_ref: String,
57 #[arg(long)]
58 request: String,
59 },
60 SimulateSafe {
61 #[arg(long, value_enum)]
62 scenario: SafeScenario,
63 },
64 ProtectRecovery {
65 #[arg(long)]
66 identity_ref: String,
67 #[arg(long)]
68 password_fd: i32,
69 },
70 Recover {
71 #[arg(long)]
72 receipt: String,
73 #[arg(long)]
74 password_fd: i32,
75 },
76 ProbeWrongRecovery {
77 #[arg(long)]
78 password_fd: i32,
79 },
80 ProbeCorruptRecovery {
81 #[arg(long)]
82 password_fd: i32,
83 },
84 Reset {
85 #[arg(long)]
86 identity_ref: String,
87 #[arg(long)]
88 receipt: String,
89 #[arg(long, value_enum)]
90 crash_after: Option<CrashBoundary>,
91 },
92 ResumeReset {
93 #[arg(long, value_enum)]
94 crash_after: Option<CrashBoundary>,
95 },
96 Safety,
97}
98
99#[derive(Debug, Copy, Clone, ValueEnum)]
100enum CrashBoundary {
101 SecretWrite,
102 SecretReadBack,
103 ManifestCommit,
104 ResetMarker,
105 ResetCommit,
106 RelaunchAcknowledge,
107}
108
109#[derive(Debug, Copy, Clone, ValueEnum)]
110enum SafeScenario {
111 ConflictCustody,
112 LostCustody,
113 LockedCustody,
114 SymlinkRefusal,
115 WeakPermissionRefusal,
116 KeychainUnavailable,
117 CorruptKeychain,
118 MalformedEventRejection,
119 UnadmittedPurposeRejection,
120 ConflictingRecoverySelection,
121 LateCompletionFencing,
122 SignerCrashBeforeCompletion,
123}
124
125impl From<SafeScenario> for ProofSafeScenario {
126 fn from(value: SafeScenario) -> Self {
127 match value {
128 SafeScenario::ConflictCustody => Self::ConflictCustody,
129 SafeScenario::LostCustody => Self::LostCustody,
130 SafeScenario::LockedCustody => Self::LockedCustody,
131 SafeScenario::SymlinkRefusal => Self::SymlinkRefusal,
132 SafeScenario::WeakPermissionRefusal => Self::WeakPermissionRefusal,
133 SafeScenario::KeychainUnavailable => Self::KeychainUnavailable,
134 SafeScenario::CorruptKeychain => Self::CorruptKeychain,
135 SafeScenario::MalformedEventRejection => Self::MalformedEventRejection,
136 SafeScenario::UnadmittedPurposeRejection => Self::UnadmittedPurposeRejection,
137 SafeScenario::ConflictingRecoverySelection => Self::ConflictingRecoverySelection,
138 SafeScenario::LateCompletionFencing => Self::LateCompletionFencing,
139 SafeScenario::SignerCrashBeforeCompletion => Self::SignerCrashBeforeCompletion,
140 }
141 }
142}
143
144impl From<CrashBoundary> for ProofCrashBoundary {
145 fn from(value: CrashBoundary) -> Self {
146 match value {
147 CrashBoundary::SecretWrite => Self::AfterSecretWrite,
148 CrashBoundary::SecretReadBack => Self::AfterSecretReadBack,
149 CrashBoundary::ManifestCommit => Self::AfterManifestCommit,
150 CrashBoundary::ResetMarker => Self::AfterResetMarker,
151 CrashBoundary::ResetCommit => Self::AfterResetCommit,
152 CrashBoundary::RelaunchAcknowledge => Self::AfterRelaunchAcknowledge,
153 }
154 }
155}
156
157#[derive(Serialize)]
158struct Outcome<'a, T: Serialize> {
159 protocol: &'static str,
160 outcome: &'a str,
161 facts: T,
162}
163
164fn main() -> ExitCode {
165 match run() {
166 Ok(()) => ExitCode::SUCCESS,
167 Err(error) => {
168 let failure = Outcome {
169 protocol: IDENTITY_PROOF_PROTOCOL,
170 outcome: "rejected",
171 facts: json!({ "error": format!("{error:#}") }),
172 };
173 eprintln!(
174 "{}",
175 serde_json::to_string(&failure).unwrap_or_else(|_| {
176 "{\"protocol\":\"openagents.omega.identity-proof.v1\",\"outcome\":\"rejected\"}".to_string()
177 })
178 );
179 ExitCode::FAILURE
180 }
181 }
182}
183
184fn run() -> Result<()> {
185 let args = Args::parse();
186 if let Command::Initialize { confirm_disposable } = &args.command {
187 if !confirm_disposable {
188 bail!("initialization requires --confirm-disposable");
189 }
190 IdentityProofService::initialize(&args.root).context("initialize disposable proof root")?;
191 return print_outcome(
192 "initialized",
193 json!({
194 "root": args.root,
195 "keyring_service": IDENTITY_PROOF_KEYRING_SERVICE,
196 "keyring_account": IDENTITY_PROOF_KEYRING_ACCOUNT,
197 }),
198 );
199 }
200
201 let crash_boundary = command_crash_boundary(&args.command);
202 let service = IdentityProofService::open(args.root, crash_boundary)
203 .context("open disposable proof root")?;
204 match args.command {
205 Command::Initialize { .. } => bail!("initialize command was not handled"),
206 Command::Create { receipt, .. } => {
207 let result = service.create(ReceiptRef::new(receipt)?)?;
208 print_outcome("create-complete", result)
209 }
210 Command::ResumeCreate => print_outcome("create-resumed", service.resume_create()?),
211 Command::Inspect => print_outcome("inspection", service.inspect()?),
212 Command::ProcessStart { .. } => {
213 print_outcome("process-start", service.inspect_for_process_start()?)
214 }
215 Command::Sign {
216 identity_ref,
217 request,
218 } => {
219 let signing_request = signing_request(IdentityRef::new(identity_ref)?, request)?;
220 print_outcome("signed", service.sign(&signing_request)?)
221 }
222 Command::ProbeForged { request } => {
223 let forged = IdentityRef::new("omega-nostr-forged-request")?;
224 rejected_signing_outcome(&service, forged, request, "forged-request-rejected")
225 }
226 Command::ProbeStale {
227 stale_identity_ref,
228 request,
229 } => rejected_signing_outcome(
230 &service,
231 IdentityRef::new(stale_identity_ref)?,
232 request,
233 "stale-request-rejected",
234 ),
235 Command::SimulateSafe { scenario } => print_outcome(
236 "safe-scenario-simulated",
237 service.simulate_safe_scenario(scenario.into()),
238 ),
239 Command::ProtectRecovery {
240 identity_ref,
241 password_fd,
242 } => print_outcome(
243 "recovery-protected",
244 service.protect_recovery(
245 &IdentityRef::new(identity_ref)?,
246 read_recovery_password(password_fd)?,
247 )?,
248 ),
249 Command::Recover {
250 receipt,
251 password_fd,
252 } => print_outcome(
253 "recovery-complete",
254 service.recover(
255 read_recovery_password(password_fd)?,
256 ReceiptRef::new(receipt)?,
257 )?,
258 ),
259 Command::ProbeWrongRecovery { password_fd } => print_outcome(
260 "wrong-recovery-password-rejected",
261 service.probe_wrong_recovery_password(read_recovery_password(password_fd)?)?,
262 ),
263 Command::ProbeCorruptRecovery { password_fd } => print_outcome(
264 "corrupt-recovery-artifact-rejected",
265 service.probe_corrupt_recovery_artifact(read_recovery_password(password_fd)?)?,
266 ),
267 Command::Reset {
268 identity_ref,
269 receipt,
270 ..
271 } => print_outcome(
272 "reset-marked",
273 service.reset(&IdentityRef::new(identity_ref)?, ReceiptRef::new(receipt)?)?,
274 ),
275 Command::ResumeReset { .. } => print_outcome("reset-resumed", service.resume_reset()?),
276 Command::Safety => print_outcome(
277 "safety-checked",
278 json!({
279 "keyring_service": IDENTITY_PROOF_KEYRING_SERVICE,
280 "keyring_account": IDENTITY_PROOF_KEYRING_ACCOUNT,
281 "production_locator_access": "rejected-by-construction",
282 }),
283 ),
284 }
285}
286
287fn read_recovery_password(file_descriptor: i32) -> Result<RecoveryPassword> {
288 if file_descriptor < 3 {
289 bail!("recovery password requires a protected file descriptor");
290 }
291 #[cfg(unix)]
292 {
293 use std::fs::File;
294 use std::os::fd::FromRawFd as _;
295 use zeroize::Zeroizing;
296
297 let mut file = unsafe { File::from_raw_fd(file_descriptor) };
298 let mut bytes = Zeroizing::new(Vec::with_capacity(1_025));
299 file.by_ref().take(1_025).read_to_end(&mut bytes)?;
300 if bytes.len() > 1_024 {
301 bail!("recovery password exceeds the proof-driver limit");
302 }
303 let mut password = Zeroizing::new(
304 String::from_utf8(std::mem::take(&mut *bytes))
305 .map_err(|_| anyhow::anyhow!("recovery password is not UTF-8"))?,
306 );
307 return RecoveryPassword::new(std::mem::take(&mut *password))
308 .map_err(|error| anyhow::anyhow!(error));
309 }
310 #[cfg(not(unix))]
311 {
312 let _ = file_descriptor;
313 bail!("protected password file descriptors are unsupported on this platform");
314 }
315}
316
317fn command_crash_boundary(command: &Command) -> Option<ProofCrashBoundary> {
318 match command {
319 Command::Create { crash_after, .. }
320 | Command::ProcessStart { crash_after }
321 | Command::Reset { crash_after, .. }
322 | Command::ResumeReset { crash_after } => crash_after.map(Into::into),
323 _ => None,
324 }
325}
326
327fn signing_request(identity_ref: IdentityRef, request: String) -> Result<AdmittedSigningRequest> {
328 Ok(AdmittedSigningRequest {
329 request_ref: ReceiptRef::new(request)?,
330 identity_ref,
331 purpose: SigningPurpose::NostrEvent,
332 event: UnsignedEventTemplate {
333 created_at: 1_700_000_000,
334 kind: 1,
335 tags: Vec::new(),
336 content: "Omega identity proof event".to_string(),
337 },
338 })
339}
340
341fn rejected_signing_outcome(
342 service: &IdentityProofService,
343 identity_ref: IdentityRef,
344 request: String,
345 outcome: &str,
346) -> Result<()> {
347 let request = signing_request(identity_ref, request)?;
348 match service.sign(&request) {
349 Ok(_) => bail!("forged or stale signing request was unexpectedly admitted"),
350 Err(error) => print_outcome(outcome, json!({ "typed_error": error.to_string() })),
351 }
352}
353
354fn print_outcome<T: Serialize>(outcome: &str, facts: T) -> Result<()> {
355 println!(
356 "{}",
357 serde_json::to_string_pretty(&Outcome {
358 protocol: IDENTITY_PROOF_PROTOCOL,
359 outcome,
360 facts,
361 })?
362 );
363 Ok(())
364}
365