}
 }
 
+#[derive(Debug,Copy,Clone)]
+struct ReportStartup;
+impl fairing::Fairing for ReportStartup {
+  fn info(&self) -> fairing::Info {
+    fairing::Info {
+      name: "ReportStartup",
+      kind: fairing::Kind::Launch,
+    }
+  }
+  fn on_launch(&self, _rocket: &Rocket) {
+    println!("{}", DAEMON_STARTUP_REPORT);
+    std::io::stdout().flush().unwrap_or_else(
+      |e| warn!("failed to report started: {:?}", &e)
+    );
+  }
+}
+
 #[throws(StartupError)]
 fn main() {
   // todo test suite for cli at least
   use structopt::StructOpt;
   #[derive(StructOpt)]
   struct Opts {
+    #[structopt(long)]
+    report_startup: bool,
+
     config_filename: Option<String>,
   }
 
 
   let rconfig = cbuilder.finalize()?;
 
-  let r = rocket::custom(rconfig)
+  let mut r = rocket::custom(rconfig)
     .attach(ContentTypeFixup)
     .attach(helmet)
     .attach(Template::fairing())
     .mount("/_/src", StaticFiles::from(&c.bundled_sources))
     ;
 
+  if opts.report_startup {
+    r = r.attach(ReportStartup);
+  }
+
   let r = otter::session::mount(r);
   let r = otter::api::mount(r);
 
 
 
 pub const DEFAULT_SENDMAIL_PROGRAM : &str = "/usr/sbin/sendmail";
 
+pub const DAEMON_STARTUP_REPORT : &str = "otter-daemon started";
+
 #[derive(Deserialize,Debug,Clone)]
 pub struct ServerConfigSpec {
   pub base_dir: Option<String>,
 
   let server_exe = ds.subst("@target@/debug/daemon-otter");
   (||{
     let mut cmd = Command::new(&server_exe);
-    cmd.arg(CONFIG);
+    cmd
+      .arg("--report-startup")
+      .arg(CONFIG);
     cln.arm_hook(&mut cmd)?;
     cmd
       .spawn().context("spawn")?;