1use std::env;
52use std::time::UNIX_EPOCH;
53
54use base64::engine::general_purpose::URL_SAFE_NO_PAD;
55use base64::Engine as _;
56use ed25519_dalek::SigningKey;
57use zeroize::Zeroizing;
58
59use cellos_core::trust_keys::{sign_event_ed25519, sign_event_hmac_sha256, SignedEventEnvelopeV1};
60use cellos_core::CloudEventV1;
61
62use crate::{GuestDeclaration, HostStamp};
63
64#[derive(Debug, Clone)]
74pub struct StampedDeclaration {
75 pub guest: GuestDeclaration,
77 pub host: HostStamp,
80}
81
82pub const PROVENANCE_DECLARED: &str = "declared";
95
96pub const ENV_SIGN_ALG: &str = "CELLOS_HOST_TELEMETRY_SIGN_ALG";
100pub const ENV_SIGN_KID: &str = "CELLOS_HOST_TELEMETRY_SIGN_KID";
102pub const ENV_SIGN_HMAC_KEY: &str = "CELLOS_HOST_TELEMETRY_SIGN_HMAC_KEY";
104pub const ENV_SIGN_ED25519_SK: &str = "CELLOS_HOST_TELEMETRY_SIGN_ED25519_SK";
106
107#[derive(Debug, thiserror::Error)]
109pub enum SignOutboundError {
110 #[error("invalid signing config: {0}")]
113 InvalidConfig(String),
114
115 #[error("signer error: {0}")]
117 Signer(String),
118
119 #[error("serialize error: {0}")]
121 Serialize(String),
122}
123
124pub enum SigningKeyMaterial {
129 Off,
131 Hmac {
133 kid: String,
135 key: Zeroizing<Vec<u8>>,
139 },
140 Ed25519 {
142 kid: String,
144 signing_key: SigningKey,
147 },
148}
149
150impl std::fmt::Debug for SigningKeyMaterial {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self {
157 SigningKeyMaterial::Off => f.debug_struct("SigningKeyMaterial::Off").finish(),
158 SigningKeyMaterial::Hmac { kid, key } => f
159 .debug_struct("SigningKeyMaterial::Hmac")
160 .field("kid", kid)
161 .field(
162 "key",
163 &format_args!("<redacted {}-byte hmac key>", key.len()),
164 )
165 .finish(),
166 SigningKeyMaterial::Ed25519 { kid, .. } => f
167 .debug_struct("SigningKeyMaterial::Ed25519")
168 .field("kid", kid)
169 .field("signing_key", &"<redacted ed25519 signing key>")
170 .finish(),
171 }
172 }
173}
174
175impl SigningKeyMaterial {
176 pub fn is_off(&self) -> bool {
178 matches!(self, SigningKeyMaterial::Off)
179 }
180
181 pub fn kid(&self) -> Option<&str> {
183 match self {
184 SigningKeyMaterial::Off => None,
185 SigningKeyMaterial::Hmac { kid, .. } => Some(kid.as_str()),
186 SigningKeyMaterial::Ed25519 { kid, .. } => Some(kid.as_str()),
187 }
188 }
189
190 pub fn from_env() -> Result<Self, SignOutboundError> {
196 let alg_raw = env::var(ENV_SIGN_ALG).unwrap_or_default();
197 let alg = alg_raw.trim().to_ascii_lowercase();
198
199 if alg.is_empty() || alg == "off" {
200 if env::var(ENV_SIGN_HMAC_KEY).is_ok() || env::var(ENV_SIGN_ED25519_SK).is_ok() {
204 return Err(SignOutboundError::InvalidConfig(format!(
205 "{ENV_SIGN_ALG} is off (or unset) but {ENV_SIGN_HMAC_KEY} \
206 or {ENV_SIGN_ED25519_SK} is set — refuse to silently \
207 drop key material; set {ENV_SIGN_ALG} explicitly"
208 )));
209 }
210 return Ok(SigningKeyMaterial::Off);
211 }
212
213 let kid = env::var(ENV_SIGN_KID).map_err(|_| {
214 SignOutboundError::InvalidConfig(format!(
215 "{ENV_SIGN_ALG}={alg_raw:?} requires {ENV_SIGN_KID} to be set"
216 ))
217 })?;
218 if kid.trim().is_empty() {
219 return Err(SignOutboundError::InvalidConfig(format!(
220 "{ENV_SIGN_KID} must be a non-empty signer kid"
221 )));
222 }
223
224 let hmac_set = env::var(ENV_SIGN_HMAC_KEY).is_ok();
225 let ed_set = env::var(ENV_SIGN_ED25519_SK).is_ok();
226 if hmac_set && ed_set {
227 return Err(SignOutboundError::InvalidConfig(format!(
228 "mutual-exclusion violated: both {ENV_SIGN_HMAC_KEY} and \
229 {ENV_SIGN_ED25519_SK} are set — pick exactly one"
230 )));
231 }
232
233 match alg.as_str() {
234 "hmac-sha256" | "hmac" => {
235 let key_b64 = env::var(ENV_SIGN_HMAC_KEY).map_err(|_| {
236 SignOutboundError::InvalidConfig(format!(
237 "{ENV_SIGN_ALG}=hmac-sha256 requires {ENV_SIGN_HMAC_KEY}"
238 ))
239 })?;
240 let trimmed = key_b64.trim().trim_end_matches('=');
241 let key: Zeroizing<Vec<u8>> =
244 Zeroizing::new(URL_SAFE_NO_PAD.decode(trimmed).map_err(|e| {
245 SignOutboundError::InvalidConfig(format!(
246 "{ENV_SIGN_HMAC_KEY} is not valid base64url: {e}"
247 ))
248 })?);
249 if key.is_empty() {
250 return Err(SignOutboundError::InvalidConfig(format!(
251 "{ENV_SIGN_HMAC_KEY} decoded to zero bytes"
252 )));
253 }
254 Ok(SigningKeyMaterial::Hmac { kid, key })
255 }
256 "ed25519" => {
257 let sk_b64 = env::var(ENV_SIGN_ED25519_SK).map_err(|_| {
258 SignOutboundError::InvalidConfig(format!(
259 "{ENV_SIGN_ALG}=ed25519 requires {ENV_SIGN_ED25519_SK}"
260 ))
261 })?;
262 let trimmed = sk_b64.trim().trim_end_matches('=');
263 let bytes = URL_SAFE_NO_PAD.decode(trimmed).map_err(|e| {
264 SignOutboundError::InvalidConfig(format!(
265 "{ENV_SIGN_ED25519_SK} is not valid base64url: {e}"
266 ))
267 })?;
268 let seed: [u8; 32] = bytes.as_slice().try_into().map_err(|_| {
269 SignOutboundError::InvalidConfig(format!(
270 "{ENV_SIGN_ED25519_SK} decoded to {} bytes, expected 32",
271 bytes.len()
272 ))
273 })?;
274 let signing_key = SigningKey::from_bytes(&seed);
275 Ok(SigningKeyMaterial::Ed25519 { kid, signing_key })
276 }
277 other => Err(SignOutboundError::InvalidConfig(format!(
278 "unknown {ENV_SIGN_ALG}={other:?} (expected off, hmac-sha256, or ed25519)"
279 ))),
280 }
281 }
282}
283
284#[derive(Debug, Clone)]
289pub enum SigningOutcome {
290 Unsigned(CloudEventV1),
292 Signed(SignedEventEnvelopeV1),
294}
295
296impl SigningOutcome {
297 pub fn event(&self) -> &CloudEventV1 {
299 match self {
300 SigningOutcome::Unsigned(ev) => ev,
301 SigningOutcome::Signed(env) => &env.event,
302 }
303 }
304}
305
306pub fn host_stamped_envelope(
313 stamped: &StampedDeclaration,
314 event_id: &str,
315 source: &str,
316 event_type: &str,
317) -> Result<CloudEventV1, SignOutboundError> {
318 let host_received_unix_secs = stamped
319 .host
320 .host_received_at
321 .duration_since(UNIX_EPOCH)
322 .map_err(|e| {
323 SignOutboundError::Serialize(format!("host_received_at predates UNIX_EPOCH: {e}"))
324 })?
325 .as_secs();
326 let host_received_at_rfc3339 = format_unix_secs_rfc3339(host_received_unix_secs);
327
328 let data = serde_json::json!({
329 "provenance": PROVENANCE_DECLARED,
330 "guest": {
331 "probeSource": stamped.guest.probe_source,
332 "guestPid": stamped.guest.guest_pid,
333 "guestComm": stamped.guest.guest_comm,
334 "guestMonotonicNs": stamped.guest.guest_monotonic_ns,
335 },
336 "host": {
337 "cellId": stamped.host.cell_id,
338 "runId": stamped.host.run_id,
339 "hostReceivedAt": host_received_at_rfc3339,
340 "specSignatureHash": stamped.host.spec_signature_hash,
341 },
342 });
343
344 Ok(CloudEventV1 {
345 specversion: "1.0".into(),
346 id: event_id.to_string(),
347 source: source.to_string(),
348 ty: event_type.to_string(),
349 datacontenttype: Some("application/json".into()),
350 data: Some(data),
351 time: Some(host_received_at_rfc3339),
352 traceparent: None,
353 })
354}
355
356pub fn sign_host_stamped_envelope(
361 envelope: CloudEventV1,
362 material: &SigningKeyMaterial,
363) -> Result<SigningOutcome, SignOutboundError> {
364 match material {
365 SigningKeyMaterial::Off => Ok(SigningOutcome::Unsigned(envelope)),
366 SigningKeyMaterial::Hmac { kid, key } => {
367 let signed = sign_event_hmac_sha256(&envelope, kid, key)
368 .map_err(|e| SignOutboundError::Signer(format!("{e}")))?;
369 Ok(SigningOutcome::Signed(signed))
370 }
371 SigningKeyMaterial::Ed25519 { kid, signing_key } => {
372 let signed = sign_event_ed25519(&envelope, kid, signing_key)
373 .map_err(|e| SignOutboundError::Signer(format!("{e}")))?;
374 Ok(SigningOutcome::Signed(signed))
375 }
376 }
377}
378
379pub fn host_stamp_and_sign(
381 stamped: &StampedDeclaration,
382 event_id: &str,
383 source: &str,
384 event_type: &str,
385 material: &SigningKeyMaterial,
386) -> Result<SigningOutcome, SignOutboundError> {
387 let envelope = host_stamped_envelope(stamped, event_id, source, event_type)?;
388 sign_host_stamped_envelope(envelope, material)
389}
390
391fn format_unix_secs_rfc3339(unix_secs: u64) -> String {
404 let secs_per_day: u64 = 86_400;
409 let days = (unix_secs / secs_per_day) as i64; let secs_of_day = unix_secs % secs_per_day;
411 let hour = (secs_of_day / 3600) as u32;
412 let minute = ((secs_of_day % 3600) / 60) as u32;
413 let second = (secs_of_day % 60) as u32;
414
415 let z = days + 719_468;
417 let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
418 let doe = (z - era * 146_097) as u64; let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146_096) / 365; let y = yoe as i64 + era * 400;
421 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); let mp = (5 * doy + 2) / 153; let d = (doy - (153 * mp + 2) / 5 + 1) as u32; let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32; let year = if m <= 2 { y + 1 } else { y };
426
427 format!(
428 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
429 year, m, d, hour, minute, second
430 )
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436 use std::collections::HashMap;
437 use std::sync::Mutex;
438 use std::time::Duration;
439
440 use cellos_core::trust_keys::verify_signed_event_envelope;
441
442 static ENV_MUTEX: Mutex<()> = Mutex::new(());
446
447 fn fixture_decl() -> StampedDeclaration {
448 StampedDeclaration {
449 guest: GuestDeclaration {
450 probe_source: "process_spawned".into(),
451 guest_pid: 4242,
452 guest_comm: "curl".into(),
453 guest_monotonic_ns: 1_234_567_890,
454 },
455 host: HostStamp {
456 cell_id: "cell-abc".into(),
457 run_id: "run-xyz".into(),
458 host_received_at: UNIX_EPOCH + Duration::from_secs(1_762_348_800),
460 spec_signature_hash: "sha256:deadbeef".into(),
461 },
462 }
463 }
464
465 fn signing_key(seed: u8) -> SigningKey {
466 SigningKey::from_bytes(&[seed; 32])
467 }
468
469 fn clear_sign_env() {
470 std::env::remove_var(ENV_SIGN_ALG);
472 std::env::remove_var(ENV_SIGN_KID);
473 std::env::remove_var(ENV_SIGN_HMAC_KEY);
474 std::env::remove_var(ENV_SIGN_ED25519_SK);
475 }
476
477 #[test]
480 fn hmac_round_trip_verifies_via_projector_path() {
481 let key = b"f4b-shared-symmetric-key-for-tests";
482 let material = SigningKeyMaterial::Hmac {
483 kid: "ops-host-telem-2026-q2".into(),
484 key: Zeroizing::new(key.to_vec()),
485 };
486 let outcome = host_stamp_and_sign(
487 &fixture_decl(),
488 "ev-hmac-001",
489 "/cellos-supervisor/host-telemetry",
490 "dev.cellos.events.cell.observability.guest.process_spawned",
491 &material,
492 )
493 .expect("sign ok");
494 let envelope = match outcome {
495 SigningOutcome::Signed(env) => env,
496 SigningOutcome::Unsigned(_) => panic!("expected Signed"),
497 };
498 assert_eq!(envelope.algorithm, "hmac-sha256");
499 assert_eq!(envelope.signer_kid, "ops-host-telem-2026-q2");
500
501 let verifying_keys: HashMap<String, _> = HashMap::new();
502 let mut hmac_keys: HashMap<String, Vec<u8>> = HashMap::new();
503 hmac_keys.insert("ops-host-telem-2026-q2".into(), key.to_vec());
504 let verified = verify_signed_event_envelope(&envelope, &verifying_keys, &hmac_keys)
505 .expect("hmac round-trip must verify");
506 assert_eq!(verified.id, "ev-hmac-001");
507 }
508
509 #[test]
512 fn ed25519_round_trip_verifies_via_projector_path() {
513 let signer = signing_key(13);
514 let material = SigningKeyMaterial::Ed25519 {
515 kid: "ops-host-telem-ed-2026-q2".into(),
516 signing_key: signer.clone(),
517 };
518 let outcome = host_stamp_and_sign(
519 &fixture_decl(),
520 "ev-ed25519-001",
521 "/cellos-supervisor/host-telemetry",
522 "dev.cellos.events.cell.observability.guest.process_spawned",
523 &material,
524 )
525 .expect("sign ok");
526 let envelope = match outcome {
527 SigningOutcome::Signed(env) => env,
528 SigningOutcome::Unsigned(_) => panic!("expected Signed"),
529 };
530 assert_eq!(envelope.algorithm, "ed25519");
531
532 let mut verifying_keys: HashMap<String, _> = HashMap::new();
533 verifying_keys.insert("ops-host-telem-ed-2026-q2".into(), signer.verifying_key());
534 let hmac_keys: HashMap<String, Vec<u8>> = HashMap::new();
535 let verified = verify_signed_event_envelope(&envelope, &verifying_keys, &hmac_keys)
536 .expect("ed25519 round-trip must verify");
537 assert_eq!(verified.id, "ev-ed25519-001");
538 }
539
540 #[test]
543 fn mutating_event_after_sign_breaks_verification() {
544 let signer = signing_key(17);
545 let material = SigningKeyMaterial::Ed25519 {
546 kid: "kid-mut".into(),
547 signing_key: signer.clone(),
548 };
549 let outcome = host_stamp_and_sign(
550 &fixture_decl(),
551 "ev-mut-001",
552 "/cellos-supervisor/host-telemetry",
553 "dev.cellos.events.cell.observability.guest.process_spawned",
554 &material,
555 )
556 .expect("sign ok");
557 let mut envelope = match outcome {
558 SigningOutcome::Signed(env) => env,
559 SigningOutcome::Unsigned(_) => panic!("expected Signed"),
560 };
561
562 if let Some(serde_json::Value::Object(map)) = envelope.event.data.as_mut() {
565 if let Some(serde_json::Value::Object(host)) = map.get_mut("host") {
566 host.insert(
567 "cellId".into(),
568 serde_json::Value::String("cell-EVIL".into()),
569 );
570 }
571 }
572
573 let mut verifying_keys: HashMap<String, _> = HashMap::new();
574 verifying_keys.insert("kid-mut".into(), signer.verifying_key());
575 let hmac_keys: HashMap<String, Vec<u8>> = HashMap::new();
576 let err = verify_signed_event_envelope(&envelope, &verifying_keys, &hmac_keys)
577 .expect_err("post-sign mutation must be rejected");
578 let msg = format!("{err}");
579 assert!(
580 msg.contains("ed25519 verify failed"),
581 "expected ed25519 verify failure, got: {msg}"
582 );
583 }
584
585 #[test]
588 fn off_mode_emits_unsigned_passthrough() {
589 let outcome = host_stamp_and_sign(
590 &fixture_decl(),
591 "ev-off-001",
592 "/cellos-supervisor/host-telemetry",
593 "dev.cellos.events.cell.observability.guest.process_spawned",
594 &SigningKeyMaterial::Off,
595 )
596 .expect("off-mode stamp ok");
597 match outcome {
598 SigningOutcome::Unsigned(ev) => {
599 assert_eq!(ev.id, "ev-off-001");
600 let data = ev.data.expect("data present");
601 assert_eq!(data["provenance"], "declared");
602 assert_eq!(data["host"]["cellId"], "cell-abc");
603 }
604 SigningOutcome::Signed(_) => panic!("Off must passthrough"),
605 }
606 }
607
608 #[test]
611 fn env_load_hmac_round_trips_a_signed_envelope() {
612 let _guard = ENV_MUTEX.lock().unwrap_or_else(|p| p.into_inner());
613 clear_sign_env();
614 let key = b"env-hmac-key-for-from_env-test";
615 std::env::set_var(ENV_SIGN_ALG, "hmac-sha256");
616 std::env::set_var(ENV_SIGN_KID, "kid-from-env");
617 std::env::set_var(ENV_SIGN_HMAC_KEY, URL_SAFE_NO_PAD.encode(key));
618
619 let material = SigningKeyMaterial::from_env().expect("from_env hmac ok");
620 clear_sign_env();
621
622 assert!(!material.is_off());
623 assert_eq!(material.kid(), Some("kid-from-env"));
624
625 let outcome = host_stamp_and_sign(
626 &fixture_decl(),
627 "ev-env-hmac",
628 "/cellos-supervisor/host-telemetry",
629 "dev.cellos.events.cell.observability.guest.process_spawned",
630 &material,
631 )
632 .expect("sign ok");
633 let envelope = match outcome {
634 SigningOutcome::Signed(env) => env,
635 SigningOutcome::Unsigned(_) => panic!("expected Signed"),
636 };
637 assert_eq!(envelope.algorithm, "hmac-sha256");
638
639 let mut hmac_keys: HashMap<String, Vec<u8>> = HashMap::new();
640 hmac_keys.insert("kid-from-env".into(), key.to_vec());
641 let verifying: HashMap<String, _> = HashMap::new();
642 verify_signed_event_envelope(&envelope, &verifying, &hmac_keys)
643 .expect("env-loaded hmac must verify");
644 }
645
646 #[test]
649 fn env_load_unset_yields_off() {
650 let _guard = ENV_MUTEX.lock().unwrap_or_else(|p| p.into_inner());
651 clear_sign_env();
652 let material = SigningKeyMaterial::from_env().expect("unset env -> Off");
653 assert!(material.is_off());
654 assert_eq!(material.kid(), None);
655
656 std::env::set_var(ENV_SIGN_ALG, "off");
658 let material2 = SigningKeyMaterial::from_env().expect("explicit off");
659 clear_sign_env();
660 assert!(material2.is_off());
661 }
662
663 #[test]
666 fn env_load_rejects_both_hmac_and_ed25519_keys_set() {
667 let _guard = ENV_MUTEX.lock().unwrap_or_else(|p| p.into_inner());
668 clear_sign_env();
669 std::env::set_var(ENV_SIGN_ALG, "ed25519");
670 std::env::set_var(ENV_SIGN_KID, "kid-conflict");
671 std::env::set_var(ENV_SIGN_HMAC_KEY, URL_SAFE_NO_PAD.encode(b"hmac"));
672 std::env::set_var(ENV_SIGN_ED25519_SK, URL_SAFE_NO_PAD.encode([0u8; 32]));
673
674 let err = SigningKeyMaterial::from_env().expect_err("both keys set must be rejected");
675 clear_sign_env();
676 let msg = format!("{err}");
677 assert!(
678 msg.contains("mutual-exclusion"),
679 "expected mutual-exclusion error, got: {msg}"
680 );
681 }
682
683 #[test]
686 fn debug_format_does_not_leak_key_bytes() {
687 let secret_pattern = b"NEVER-EVER-LOG-THIS-SECRET-XYZZY";
688 let hmac = SigningKeyMaterial::Hmac {
689 kid: "kid-redact".into(),
690 key: Zeroizing::new(secret_pattern.to_vec()),
691 };
692 let dbg = format!("{:?}", hmac);
693 assert!(
694 !dbg.contains("NEVER-EVER-LOG-THIS-SECRET"),
695 "Debug for Hmac MUST NOT print key bytes: {dbg}"
696 );
697 assert!(dbg.contains("kid-redact"));
698 assert!(dbg.contains("redacted"));
699
700 let signer = signing_key(99);
702 let ed = SigningKeyMaterial::Ed25519 {
703 kid: "kid-ed-redact".into(),
704 signing_key: signer.clone(),
705 };
706 let dbg_ed = format!("{:?}", ed);
707 let seed_hex: String = signer
710 .to_bytes()
711 .iter()
712 .map(|b| format!("{:02x}", b))
713 .collect();
714 assert!(
715 !dbg_ed.contains(&seed_hex),
716 "Debug for Ed25519 MUST NOT print key bytes: {dbg_ed}"
717 );
718 assert!(dbg_ed.contains("kid-ed-redact"));
719 assert!(dbg_ed.contains("redacted"));
720
721 let off = SigningKeyMaterial::Off;
723 let dbg_off = format!("{:?}", off);
724 assert!(dbg_off.contains("Off"));
725 }
726
727 #[test]
730 fn end_to_end_guest_to_projector_verify() {
731 let signer = signing_key(23);
733 let material = SigningKeyMaterial::Ed25519 {
734 kid: "kid-e2e".into(),
735 signing_key: signer.clone(),
736 };
737 let stamped = fixture_decl();
738
739 let outcome = host_stamp_and_sign(
740 &stamped,
741 "ev-e2e-001",
742 "/cellos-supervisor/host-telemetry",
743 "dev.cellos.events.cell.observability.guest.process_spawned",
744 &material,
745 )
746 .expect("e2e sign ok");
747 let signed = match outcome {
748 SigningOutcome::Signed(env) => env,
749 SigningOutcome::Unsigned(_) => panic!("expected Signed"),
750 };
751
752 let wire = serde_json::to_vec(&signed).expect("serialize");
754 let arrived: SignedEventEnvelopeV1 = serde_json::from_slice(&wire).expect("deserialize");
755
756 let mut verifying: HashMap<String, _> = HashMap::new();
758 verifying.insert("kid-e2e".into(), signer.verifying_key());
759 let hmac_keys: HashMap<String, Vec<u8>> = HashMap::new();
760 let event = verify_signed_event_envelope(&arrived, &verifying, &hmac_keys)
761 .expect("projector verify");
762
763 assert_eq!(event.id, "ev-e2e-001");
767 assert_eq!(
768 event.ty,
769 "dev.cellos.events.cell.observability.guest.process_spawned"
770 );
771 let data = event.data.as_ref().expect("data");
772 assert_eq!(data["provenance"], "declared");
773 assert_eq!(data["host"]["cellId"], "cell-abc");
774 assert_eq!(data["host"]["runId"], "run-xyz");
775 assert_eq!(data["host"]["specSignatureHash"], "sha256:deadbeef");
776 assert_eq!(data["guest"]["probeSource"], "process_spawned");
777 }
778
779 #[test]
782 fn rfc3339_formatter_matches_known_anchors() {
783 assert_eq!(format_unix_secs_rfc3339(0), "1970-01-01T00:00:00Z");
785
786 assert_eq!(format_unix_secs_rfc3339(86_400), "1970-01-02T00:00:00Z");
788
789 assert_eq!(format_unix_secs_rfc3339(3_661), "1970-01-01T01:01:01Z");
791
792 assert_eq!(
795 format_unix_secs_rfc3339(946_684_800),
796 "2000-01-01T00:00:00Z"
797 );
798
799 assert_eq!(
804 format_unix_secs_rfc3339(1_582_979_696),
805 "2020-02-29T12:34:56Z"
806 );
807 }
808}