#![doc = include_str!("../Documentation.md")]
use kcode_gemini_3_1_pro::Gemini31Pro;
use kcode_k1_access::K1Access;
use kcode_k1_access_full_audio::K1AccessFullAudio;
use kcode_k1_access_persons::K1AccessPersons;
use kcode_k1_access_profiles::K1AccessProfiles;
use kcode_k1_accounting::Accounting;
use kcode_k1_accounts::K1Accounts;
use kcode_k1_audio_classification::AudioClassification;
use kcode_k1_chat_service::K1ChatService;
use kcode_k1_codex_adapter::{Adapter as CodexAdapter, Error as CodexAdapterError};
use kcode_k1_daemon_files::DaemonFiles;
use kcode_k1_daemon_http_boundary::{
Boundary, PUBLIC_ORIGIN, api_not_found, warn_if_slow, write_readiness,
};
use kcode_k1_daemon_provider_config::{
CODEX_EXECUTABLE_ENV, audio_models, codex_configs, codex_executable, resolve_ffmpeg,
};
use kcode_k1_full_audio::K1FullAudio;
use kcode_k1_groups::K1Groups;
use kcode_k1_http::{Config as HttpConfig, K1Http};
use kcode_k1_http_accounts::K1HttpAccounts;
use kcode_k1_http_people::K1HttpPeople;
use kcode_k1_http_replay::{ReplayConfig, ReplayWindow};
use kcode_k1_invites::K1Invites;
use kcode_k1_objects::K1Objects;
use kcode_k1_peering::K1Peering;
use kcode_k1_persons::K1Persons;
use kcode_k1_txn_ordering::K1TxnOrdering;
use kcode_k1_users::K1Users;
use kcode_k1_vault::{ExposeSecret, K1Vault, SecretString};
use kcode_speaker_v3_analysis::Analyzer;
use std::fmt;
use std::path::{Path, PathBuf};
use std::process::ExitCode;
use std::sync::Arc;
use std::time::Instant;
const INVITE_LINK_URL: &str = "http://localhost:4321/lib/kcode-k1-ui/*/account.html";
const GEMINI_API_KEY: &str = "gemini-api-key";
struct Prepared {
boundary: Boundary,
unused_invites: usize,
vault: Arc<K1Vault>,
chat: K1ChatService,
}
enum StartupError {
Generic,
CodexAdapter(CodexAdapterError),
Chat(String),
}
impl From<()> for StartupError {
fn from((): ()) -> Self {
Self::Generic
}
}
impl fmt::Display for StartupError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Generic => formatter.write_str("kcode-k1-daemon: startup failed"),
Self::CodexAdapter(error) => {
write!(formatter, "kcode-k1-daemon: startup failed: {error}")
}
Self::Chat(child) => {
write!(formatter, "kcode-k1-daemon: startup failed: {child}")
}
}
}
}
pub fn run(k1_root: PathBuf) -> ExitCode {
let runtime = match tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
{
Ok(runtime) => runtime,
Err(_) => {
eprintln!("kcode-k1-daemon: startup failed");
return ExitCode::from(1);
}
};
let passphrase = match rpassword::prompt_password("Unlock K1 vault: ") {
Ok(passphrase) => match protect_passphrase(passphrase) {
Ok(passphrase) => passphrase,
Err(()) => {
eprintln!("kcode-k1-daemon: startup failed");
return ExitCode::from(1);
}
},
Err(_) => {
eprintln!("kcode-k1-daemon: startup failed");
return ExitCode::from(1);
}
};
runtime.block_on(run_async(k1_root, passphrase))
}
fn protect_passphrase(passphrase: String) -> Result<SecretString, ()> {
(!passphrase.is_empty())
.then(|| SecretString::from(passphrase))
.ok_or(())
}
async fn run_async(k1_root: PathBuf, passphrase: SecretString) -> ExitCode {
let started = Instant::now();
let prepared = match startup(k1_root, passphrase).await {
Ok(prepared) => prepared,
Err(error) => {
warn_if_slow(started.elapsed(), "error");
eprintln!("{error}");
return ExitCode::from(1);
}
};
let elapsed = started.elapsed();
if write_readiness(prepared.unused_invites).is_err() {
warn_if_slow(elapsed, "error");
eprintln!("kcode-k1-daemon: startup failed");
return ExitCode::from(1);
}
warn_if_slow(elapsed, "ready");
let Prepared {
boundary,
vault,
chat,
..
} = prepared;
let result = boundary.serve().await;
drop((chat, vault));
match result {
Ok(()) => ExitCode::SUCCESS,
Err(()) => {
eprintln!("kcode-k1-daemon: listener failed");
ExitCode::from(1)
}
}
}
async fn startup(k1_root: PathBuf, passphrase: SecretString) -> Result<Prepared, StartupError> {
let state_root = state_root(&k1_root);
let files = DaemonFiles::open(&state_root).map_err(|_| ())?;
let ordering = Arc::new(K1TxnOrdering::open(&state_root.join("ordering")).map_err(|_| ())?);
let peering = Arc::new(
K1Peering::open(&state_root.join("peering"), Arc::clone(&ordering)).map_err(|_| ())?,
);
let vault = open_vault(
&state_root,
passphrase,
Arc::clone(&ordering),
Arc::clone(&peering),
)?;
let persons = Arc::new(
K1Persons::open(
&state_root.join("persons"),
Arc::clone(&ordering),
Arc::clone(&peering),
)
.map_err(|_| ())?,
);
let invites = Arc::new(
K1Invites::open(
&state_root.join("invites"),
Arc::clone(&ordering),
Arc::clone(&peering),
)
.map_err(|_| ())?,
);
let accounts = Arc::new(K1Accounts::open(Arc::clone(&invites)).map_err(|_| ())?);
let users = Arc::new(K1Users::new(Arc::clone(&accounts), Arc::clone(&persons)));
let groups = Arc::new(
K1Groups::open(
&state_root.join("groups"),
Arc::clone(&ordering),
Arc::clone(&peering),
)
.map_err(|_| ())?,
);
let profiles = Arc::new(
K1AccessProfiles::open(
&state_root.join("access-profiles"),
Arc::clone(&ordering),
Arc::clone(&peering),
)
.map_err(|_| ())?,
);
let gemini_key = vault.secret(GEMINI_API_KEY).map_err(|_| ())?.ok_or(())?;
let gemini = Gemini31Pro::new(
gemini_key.expose_secret().to_owned(),
Accounting::new(),
std::time::Duration::from_secs(30 * 60),
)
.map_err(|_| ())?;
let executable = codex_executable(std::env::var_os(CODEX_EXECUTABLE_ENV));
let working_directory = std::env::current_dir()
.map_err(|_| ())?
.to_string_lossy()
.into_owned();
let (audio_config, chat_config) = codex_configs(executable, working_directory);
let audio_codex_adapter = CodexAdapter::open(audio_config)
.await
.map_err(StartupError::CodexAdapter)?;
let chat_codex_adapter = audio_codex_adapter
.with_config(chat_config)
.map_err(StartupError::CodexAdapter)?;
let analyzer = Analyzer::from_codex_adapter(gemini, audio_codex_adapter);
let objects =
Arc::new(K1Objects::open(Arc::clone(&ordering), Arc::clone(&peering)).map_err(|_| ())?);
let classification = Arc::new(
AudioClassification::open(
&state_root.join("audio-classification"),
Arc::clone(&ordering),
Arc::clone(&peering),
Arc::clone(&objects),
analyzer,
)
.map_err(|_| ())?,
);
let full_audio = Arc::new(
K1FullAudio::open(
resolve_ffmpeg()?,
Arc::clone(&objects),
Arc::clone(&classification),
)
.map_err(|_| ())?,
);
let access = Arc::new(
K1Access::open(
&state_root.join("access"),
Arc::clone(&ordering),
Arc::clone(&peering),
Arc::clone(&groups),
)
.map_err(|_| ())?,
);
let chat = K1ChatService::open(
&state_root.join("chat"),
Arc::clone(&ordering),
Arc::clone(&peering),
Arc::clone(&access),
Arc::clone(&profiles),
chat_codex_adapter,
)
.map_err(StartupError::Chat)?;
let access_persons = Arc::new(
K1AccessPersons::open(
Arc::clone(&access),
Arc::clone(&profiles),
Arc::clone(&persons),
)
.map_err(|_| ())?,
);
let models = audio_models();
let audio = Arc::new(
K1AccessFullAudio::open_for_models(
Arc::clone(&access),
Arc::clone(&profiles),
full_audio,
classification,
Arc::clone(&groups),
models.to_vec(),
)
.map_err(|_| ())?,
);
let replay = ReplayWindow::open(ReplayConfig {
epoch_file: files.replay_epoch_path().to_owned(),
max_nonces_per_epoch: usize::MAX,
})
.await
.map_err(|_| ())?;
let unused_invites = kcode_k1_daemon_invite_stock::reconcile(
&invites,
files.invite_links_path(),
INVITE_LINK_URL,
)
.map_err(|_| ())?;
if unused_invites < 100 {
return Err(().into());
}
let adapter = K1HttpAccounts::new(
Arc::clone(&accounts),
Arc::clone(&invites),
Arc::clone(&users),
);
let people = K1HttpPeople::new(accounts, users, groups, profiles);
let http = K1Http::new(
HttpConfig {
server_id: files.server_id().to_owned(),
public_origin: PUBLIC_ORIGIN.to_owned(),
max_body_bytes: usize::MAX,
},
replay,
adapter.identity_provider(),
)
.map_err(|_| ())?;
let person_routes =
kcode_k1_http_persons::authenticated_routes(access_persons, access, models[0])
.map_err(|_| ())?;
let authenticated = adapter
.authenticated_routes()
.merge(people.authenticated_routes())
.merge(kcode_k1_http_audio::authenticated_routes(audio))
.merge(person_routes)
.fallback(api_not_found);
let api = http.router(
adapter.registration_endpoint(),
kcode_k1_terms::endpoint(),
authenticated,
);
let boundary = Boundary::bind(api, files.server_id().to_owned())
.await
.map_err(|_| ())?;
Ok(Prepared {
boundary,
unused_invites,
vault,
chat,
})
}
fn open_vault(
state_root: &Path,
passphrase: SecretString,
ordering: Arc<K1TxnOrdering>,
peering: Arc<K1Peering>,
) -> Result<Arc<K1Vault>, ()> {
K1Vault::open(&state_root.join("vault"), passphrase, ordering, peering)
.map(Arc::new)
.map_err(|_| ())
}
fn state_root(k1_root: &Path) -> PathBuf {
k1_root.join("state")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn public_operation_and_state_root_are_fixed() {
let _: fn(PathBuf) -> ExitCode = run;
assert_eq!(
state_root(Path::new("/trusted/k1")),
PathBuf::from("/trusted/k1/state")
);
}
#[test]
fn accepted_passphrase_boundary_is_strict_and_protected() {
assert!(protect_passphrase(String::new()).is_err());
let text = "conspicuous-fake-passphrase-never-real";
let protected = protect_passphrase(text.to_owned()).unwrap();
assert!(!format!("{protected:?}").contains(text));
}
#[test]
fn vault_composition_persists_at_the_fixed_path() {
let root =
std::env::temp_dir().join(format!("kcode-k1-daemon-vault-test-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
let state = state_root(&root);
assert_eq!(state.join("vault"), root.join("state/vault"));
let parts = || {
let ordering = Arc::new(K1TxnOrdering::open(&state.join("ordering")).unwrap());
let peering =
Arc::new(K1Peering::open(&state.join("peering"), ordering.clone()).unwrap());
(ordering, peering)
};
let password = || SecretString::from("fake-test-password-never-real");
let (ordering, peering) = parts();
let vault = open_vault(&state, password(), ordering.clone(), peering.clone()).unwrap();
vault
.set(
"fake-provider-secret",
SecretString::from("conspicuous-fake-value-never-real"),
)
.unwrap();
drop((vault, peering, ordering));
let (ordering, peering) = parts();
let vault = open_vault(&state, password(), ordering.clone(), peering.clone()).unwrap();
drop((vault, peering, ordering));
let (ordering, peering) = parts();
assert!(
open_vault(
&state,
SecretString::from("wrong-fake-password-never-real"),
ordering,
peering
)
.is_err()
);
std::fs::remove_dir_all(root).unwrap();
}
#[test]
fn fixed_provider_key_and_origins_remain_exact_and_distinct() {
assert_eq!(GEMINI_API_KEY, "gemini-api-key");
assert_eq!(
INVITE_LINK_URL,
"http://localhost:4321/lib/kcode-k1-ui/*/account.html"
);
assert_eq!(PUBLIC_ORIGIN, "http://localhost:4450");
assert_ne!(INVITE_LINK_URL, PUBLIC_ORIGIN);
}
#[test]
fn startup_error_rendering_preserves_safe_adapter_and_chat_messages() {
assert_eq!(
StartupError::from(()).to_string(),
"kcode-k1-daemon: startup failed"
);
let error = CodexAdapterError {
kind: kcode_k1_codex_adapter::ErrorKind::Unavailable,
message: "safe adapter display".to_owned(),
diagnostics: b"RAW_SECRET_DIAGNOSTIC".to_vec(),
};
let rendered = StartupError::CodexAdapter(error).to_string();
assert_eq!(
rendered,
"kcode-k1-daemon: startup failed: safe adapter display"
);
assert!(!rendered.contains("RAW_SECRET_DIAGNOSTIC"));
let rendered =
StartupError::Chat("open chat service: safe child failure".to_owned()).to_string();
assert_eq!(
rendered,
"kcode-k1-daemon: startup failed: open chat service: safe child failure"
);
}
#[test]
fn selected_composition_dependencies_and_constructor_are_compatible() {
const MANIFEST: &str = include_str!("../Cargo.toml");
for selected in [
"kcode-k1-access-full-audio = \"0.7.3\"",
"kcode-k1-audio-classification = \"0.5.5\"",
"kcode-k1-chat-service = \"0.2.0\"",
"kcode-k1-codex-adapter = \"0.5.0\"",
"kcode-k1-daemon-http-boundary = \"0.1.0\"",
"kcode-k1-daemon-provider-config = \"0.1.0\"",
"kcode-k1-full-audio = \"0.3.6\"",
"kcode-k1-http-audio = \"0.1.4\"",
"kcode-speaker-v3-analysis = { version = \"0.3.4\", default-features = false, features = [\"adapter-providers\"] }",
] {
assert!(MANIFEST.contains(selected));
}
assert!(!MANIFEST.lines().any(|line| {
line.trim_start()
.starts_with("kcode-speaker-v3-terra-analysis ")
}));
fn require_constructor(_: fn(Gemini31Pro, CodexAdapter) -> Analyzer) {}
require_constructor(Analyzer::from_codex_adapter);
}
}