Files
inbuxa-server/crates/store/src/build/registry.rs
T
jcoffey-dev a993f9ab01
ci / fork-checks (pull_request) Successful in 47s
ci / build (pull_request) Successful in 5m4s
Write the brand in lowercase where people see it
The name is inbuxa, lowercase, like the wordmark; INBUXA reads as an
acronym. The admin and webmail already changed. Here that's everything
the server shows people: the brand macro behind the protocol greetings,
the HTTP and SCIM realms, the startup banner and the calendar and contact
PRODID; the first-party OAuth client descriptions; the legacy-protocol
refusals; the default calendar and address book names and the SMTP
greeting default, in the code and the schema served to the admin
(checksum regenerated); startup and shutdown events; the User-Agent;
the sign-in and RSVP pages; the service units; the OpenAPI realm; the
crate descriptions and the README, where it's set in bold.

Identifiers that are uppercase for their own reasons stay: INBUXA_*
settings, SUBSPACE_INBUXA. So do code comments and the AGPL 5(a) notice
lines.

Tests follow: the IMAP ID name, the default collection names, the PRODID
in the iTIP fixtures and the CalDAV free-busy expectations, and the e2e
legacy-protocol refusals. The webdav, imap and jmap suites pass, so do
the unit tests of every crate touched, and 73 of 75 SMTP tests; of the
other two, antispam fails on main too, and queue_retry is a timing flake
that passes on its own.
2026-09-22 20:08:10 -07:00

182 lines
6.2 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*
* Modified by Coffey Labs in 2026 for INBUXA.
*/
use crate::{
RegistryStore, RegistryStoreInner, Store, backend::ephemeral::EphemeralStore,
registry::local::RegistryInit,
};
use rand::{RngExt, distr::Alphanumeric, rng};
use std::path::PathBuf;
impl RegistryStore {
pub async fn init(local: PathBuf, acquire_node_id: bool) -> Result<Self, String> {
// Create inner store
let mut inner = RegistryStoreInner::new(local);
// Build store
inner.store = match inner.read_data_store().await {
RegistryInit::Ok(data_store) => {
// The recovery administrator exists for first-boot setup and
// for recovery mode. On a configured server running normally
// it would be a standing full-admin login for whoever knows the
// variable, so it's ignored there, and loudly.
if !inner.env_recovery_mode && inner.env_recovery_admin.take().is_some() {
eprintln!();
eprintln!("⚠️ INBUXA_RECOVERY_ADMIN is set, but the");
eprintln!(" server is configured and not in recovery mode, so it is ignored.");
eprintln!(" Remove it from the environment. To use it for recovery, also set");
eprintln!(" INBUXA_RECOVERY_MODE=1.");
eprintln!();
}
Store::build(data_store).await?
}
RegistryInit::Err(err) => return Err(err),
RegistryInit::Bootstrap => {
inner.env_recovery_mode = true;
if inner.env_recovery_admin.is_none() {
let password = rng()
.sample_iter(Alphanumeric)
.take(16)
.map(char::from)
.collect::<String>();
eprintln!();
eprintln!("════════════════════════════════════════════════════════════");
eprintln!("🔑 inbuxa bootstrap mode - temporary administrator account");
eprintln!();
eprintln!(" username: admin");
eprintln!(" password: {password}");
eprintln!();
eprintln!("Use these credentials to complete the initial setup at the");
eprintln!("/admin web UI. Once setup is done, the server will provision a");
eprintln!("permanent administrator and this temporary account will no");
eprintln!("longer apply.");
eprintln!();
eprintln!("This password is shown only once. To pin a credential");
eprintln!("instead, set INBUXA_RECOVERY_ADMIN=admin:<password> in the");
eprintln!("env file.");
eprintln!("════════════════════════════════════════════════════════════");
eprintln!();
inner.env_recovery_admin = Some(("admin".to_string(), password));
}
EphemeralStore::open()
}
};
Self::from_inner(inner, acquire_node_id).await
}
pub fn from_inner_bootstrapped(inner: RegistryStoreInner) -> Self {
Self(inner.into())
}
pub async fn from_inner(
mut inner: RegistryStoreInner,
acquire_node_id: bool,
) -> Result<Self, String> {
// Create tables (SQL only)
inner
.store
.create_tables()
.await
.map_err(|err| format!("Failed to create tables: {err}"))?;
if acquire_node_id {
inner.acquire_node_id().await?;
}
Ok(Self(inner.into()))
}
#[inline(always)]
pub fn recovery_admin(&self) -> Option<&(String, String)> {
self.0.env_recovery_admin.as_ref()
}
#[inline(always)]
pub fn cluster_role(&self) -> Option<&str> {
self.0.env_cluster_role.as_deref()
}
#[inline(always)]
pub fn cluster_push_shard(&self) -> u32 {
self.0.env_push_shard_id
}
#[inline(always)]
pub fn local_hostname(&self) -> &str {
&self.0.env_hostname
}
#[inline(always)]
pub fn public_url(&self) -> Option<&str> {
self.0.env_public_url.as_deref()
}
#[inline(always)]
pub fn is_recovery_mode(&self) -> bool {
self.0.env_recovery_mode
}
#[inline(always)]
pub fn is_bootstrap_mode(&self) -> bool {
self.0.store.is_ephemeral()
}
#[inline(always)]
pub fn path(&self) -> &PathBuf {
&self.0.local_path
}
#[inline(always)]
pub fn store(&self) -> &Store {
&self.0.store
}
pub fn initialize_inner(&self, store: Store) -> RegistryStoreInner {
let mut inner = self.0.as_ref().clone();
inner.store = store;
inner
}
#[cfg(feature = "test_mode")]
pub fn clone_with_public_url(&self, url: String) -> Self {
let mut inner = self.0.as_ref().clone();
inner.env_public_url = Some(url);
Self(inner.into())
}
#[cfg(feature = "test_mode")]
pub async fn new(
path: &str,
store: Store,
hostname: String,
push_shard_id: u32,
cluster_role: Option<String>,
) -> Self {
Self::from_inner(
RegistryStoreInner {
local_path: PathBuf::from(path),
store,
node_id: 0,
env_recovery_mode: false,
env_recovery_admin: Some(("admin".to_string(), "popolna_zapora".to_string())),
env_cluster_role: cluster_role,
env_push_shard_id: push_shard_id,
env_hostname: hostname,
env_public_url: None,
id_generator: utils::snowflake::SnowflakeIdGenerator::new(),
},
true,
)
.await
.unwrap()
}
}