Skip to main content

confium_log_server/
main.rs

1//! `confium-log-server` — public transparency log server.
2//!
3//! Reference implementation of `log.confium.org`. An append-only
4//! Merkle transparency log (RFC 6962 / RFC 9162) with first-class
5//! support for certificate entries — every Confium-issued cert
6//! (CNML, code-signing, SSH, document-signing, TLS) gets anchored
7//! automatically and is queryable by fingerprint.
8//!
9//! ## Architecture
10//!
11//! Single binary, embedded SQLite storage, no external services to
12//! operate. The Merkle tree is materialized incrementally on each
13//! append; reads serve from in-memory cache.
14//!
15//! ## Quickstart
16//!
17//! ```sh
18//! $ cargo run -p confium-log-server -- --db /var/lib/confium/log.db --listen 0.0.0.0:8080
19//! # listening on http://0.0.0.0:8080
20//! ```
21//!
22//! ## API
23//!
24//! ### Hash entries (generic)
25//!
26//! `POST /v1/append` — append a SHA-256 hash
27//! `GET /v1/head` — current tree head
28//! `GET /v1/proof/<sequence>` — inclusion proof
29//! `GET /v1/consistency/<old_size>` — consistency proof
30//!
31//! ### Certificate entries (cert-aware)
32//!
33//! `POST /v1/certificates` — append a DER-encoded X.509 cert
34//! `GET /v1/certificates/<fingerprint>` — lookup by SHA-256 fingerprint
35//! `GET /v1/issuers/<issuer>/certificates` — list certs by issuer DN
36//!
37//! ### Bitcoin OTS anchoring
38//!
39//! `GET /v1/head/<sequence>/ots` — OTS proof for tree head at sequence
40//!
41//! ### Witness gossip
42//!
43//! `POST /v1/head/<sequence>/witness` — submit a witness countersignature
44//! `GET /v1/head/<sequence>/witnesses` — list known witnesses for tree head
45
46// Several log-server helpers (pagination field, witness digest helpers,
47// historical entry lookup) are pub for the upcoming HTTP API expansion
48// but not yet wired into a route handler.
49#![forbid(unsafe_code)]
50#![allow(dead_code)]
51#![allow(missing_docs)]
52
53mod api;
54mod cert;
55mod db;
56#[cfg(feature = "postgres")]
57mod db_pg;
58mod merkle;
59mod ots_anchor;
60mod witness;
61
62use clap::Parser;
63use std::path::PathBuf;
64use std::sync::Arc;
65
66use api::AppState;
67
68/// Command-line arguments for the log server.
69#[derive(Parser, Debug)]
70#[command(
71    name = "confium-log-server",
72    version,
73    about = "Public transparency log server for Confium"
74)]
75pub struct Args {
76    /// Path to the SQLite database file. Created if missing.
77    #[arg(long, default_value = "confium-log.db")]
78    pub db: PathBuf,
79
80    /// Address to listen on.
81    #[arg(long, default_value = "127.0.0.1:8080")]
82    pub listen: String,
83
84    /// Disable the periodic OTS anchor (useful for testing).
85    #[arg(long)]
86    pub no_ots: bool,
87
88    /// Interval between OTS anchor submissions, in seconds.
89    #[arg(long, default_value_t = 600)]
90    pub ots_interval_secs: u64,
91
92    /// Maximum entries per paged response.
93    #[arg(long, default_value_t = 1000)]
94    pub page_size: usize,
95}
96
97#[tokio::main]
98async fn main() -> Result<(), Box<dyn std::error::Error>> {
99    tracing_subscriber::fmt()
100        .with_env_filter(
101            tracing_subscriber::EnvFilter::try_from_default_env()
102                .unwrap_or_else(|_| "confium_log_server=info,tower_http=info".into()),
103        )
104        .init();
105
106    let args = Args::parse();
107    tracing::info!(?args.db, ?args.listen, "starting confium-log-server");
108
109    let db = db::Database::open(&args.db)?;
110    db.init_schema()?;
111    let merkle = merkle::MerkleState::from_db(&db)?;
112    let state = Arc::new(AppState {
113        db,
114        merkle: parking_lot::Mutex::new(merkle),
115        page_size: args.page_size,
116    });
117
118    // Background OTS anchor task. Skipped when --no-ots is set.
119    if !args.no_ots {
120        let anchor_state = state.clone();
121        tokio::spawn(async move {
122            ots_anchor::run_anchor_loop(
123                anchor_state,
124                std::time::Duration::from_secs(args.ots_interval_secs),
125            )
126            .await;
127        });
128    }
129
130    let app = api::router(state);
131    let listener = tokio::net::TcpListener::bind(&args.listen).await?;
132    tracing::info!("listening on http://{}", args.listen);
133    axum::serve(listener, app).await?;
134    Ok(())
135}