kcode-k1-loom-bootstrap 0.1.1

First-run Loom administrator, public group, root, and Rust source bootstrap
Documentation
#![doc = include_str!("../Documentation.md")]
#![forbid(unsafe_code)]

use kcode_k1_access_kmap::K1AccessKmap;
use kcode_k1_access_launch_nodes::{
    AccessContext, AccessId, AccessPolicy, Authority, FilteredAuthorities, K1AccessLaunchNodes,
    ModelId, ProfileId, TargetId, TargetName, TxId, UserId, ViewerSubject,
};
use kcode_k1_access_profiles::{K1AccessProfiles, ProfileColor, ProfileName};
use kcode_k1_bootstrap_identity::{BootstrapIdentity, prompt};
use kcode_k1_bootstrap_state::{
    BeginRecord, BootstrapStatus, CompleteRecord, ImportedPackage as StatePackage, K1BootstrapState,
};
use kcode_k1_groups::{
    ALL_MODELS, ALL_MODELS_MEMBER, ALL_USERS, Group, GroupId, GroupName, GroupRole, K1Groups,
};
use kcode_k1_invites::K1Invites;
use kcode_k1_launch_nodes::LaunchNodes;
use kcode_k1_rust_bootstrap_archive::{LoadedArchive, read};
use kcode_k1_rust_bootstrap_import::{PreparedPackage, import_all, prepare};
use kcode_k1_rust_projection::K1RustProjection;
use kcode_k1_users::{K1Users, NewUser, User};
use std::path::Path;

const GROUP_NAME: &str = "loom-devs";
const PROFILE_NAME: &str = "loom-devs";
const ROOT_TITLE: &str = "loom-devs Kmap Root";
const ROOT_HINT: &str = "The public starting point for the loom-devs group Kmap.";
const ROOT_NARRATIVE: &str = "This is the public root of the loom-devs group Kmap.";
const LAUNCH_TARGET: &str = "KmapLaunchNode";

pub struct BootstrapServices<'a> {
    pub state: &'a K1BootstrapState,
    pub invites: &'a K1Invites,
    pub users: &'a K1Users,
    pub groups: &'a K1Groups,
    pub profiles: &'a K1AccessProfiles,
    pub access_kmap: &'a K1AccessKmap,
    pub access_launch_nodes: &'a K1AccessLaunchNodes,
    pub launch_nodes: &'a LaunchNodes,
    pub rust_projection: &'a K1RustProjection,
    pub model: ModelId,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BootstrapResult {
    record: CompleteRecord,
    completed_now: bool,
}

impl BootstrapResult {
    pub fn record(&self) -> &CompleteRecord {
        &self.record
    }

    pub const fn completed_now(&self) -> bool {
        self.completed_now
    }
}

pub fn ensure(root: &Path, services: BootstrapServices<'_>) -> Result<BootstrapResult, String> {
    if let BootstrapStatus::Complete { record, .. } = services.state.status()? {
        return Ok(BootstrapResult {
            record,
            completed_now: false,
        });
    }

    let loaded = load_archive(root)?;
    let identity = prompt(kcode_k1_terms::text().as_bytes())?;
    let begin = BeginRecord::new(
        loaded.sha256,
        identity.username().to_owned(),
        identity.full_name().to_owned(),
        identity.public_key(),
    );
    services.state.begin(begin.clone())?;

    let user = reconcile_user(services.invites, services.users, &identity)?;
    let account_user_bytes = *user.user_id().as_tx_id().as_bytes();
    let user_id = UserId::from_tx_id(TxId::from_bytes(account_user_bytes));

    let group = reconcile_group(services.groups, user_id)?;
    let group_id = group.id();
    services
        .groups
        .set_model_membership(user_id, group_id, ALL_MODELS_MEMBER, true)?;

    let policy = public_policy(group_id)?;
    let profile_id = reconcile_profile(services.profiles, user_id, group_id, &policy)?;
    let prepared = prepare(&loaded.archive, *group_id.txid().as_bytes())?;
    let context = AccessContext::new(user_id, services.model, FilteredAuthorities::empty())?;
    let root_id = reconcile_root(&services, &context, profile_id, &policy, group_id)?;
    let imported = import_all(services.rust_projection, &prepared)?;
    verify_inventory(&prepared, &imported)?;

    let inventory = imported
        .into_iter()
        .map(|package| {
            StatePackage::new(
                package.logical_name().to_owned(),
                package.version().clone(),
                package.source_sha256(),
            )
        })
        .collect::<Result<Vec<_>, _>>()?;

    let record = CompleteRecord::new(
        begin,
        account_user_bytes,
        *group_id.txid().as_bytes(),
        *profile_id.txid().as_bytes(),
        *root_id.txid().as_bytes(),
        inventory,
    )?;
    services.state.complete(record.clone())?;

    Ok(BootstrapResult {
        record,
        completed_now: true,
    })
}

fn load_archive(root: &Path) -> Result<LoadedArchive, String> {
    let loaded = read(&root.join("bootstrap/k1-rust-code.zip"))?;
    loaded.archive.require_complete()?;
    if loaded.archive.root_library != "loom" {
        return Err("bootstrap archive root library is not loom".to_owned());
    }
    Ok(loaded)
}

fn reconcile_user(
    invites: &K1Invites,
    users: &K1Users,
    identity: &BootstrapIdentity,
) -> Result<User, String> {
    if let Some(existing) = users.find_by_username(identity.username())? {
        require_matching_user(&existing, identity)?;
        return Ok(existing);
    }

    let (_, invite) = invites.create()?;
    let candidate = NewUser::new(
        identity.username(),
        identity.full_name(),
        identity.public_key(),
        kcode_k1_terms::REVISION,
        kcode_k1_terms::sha256(),
        identity.message_signature(),
    )?;
    let user = users.register(&invite, candidate)?;
    require_matching_user(&user, identity)?;
    Ok(user)
}

fn require_matching_user(user: &User, identity: &BootstrapIdentity) -> Result<(), String> {
    let matches = user.username() == identity.username()
        && user.full_name() == identity.full_name()
        && user.public_key() == identity.public_key()
        && user.tos_revision() == kcode_k1_terms::REVISION
        && user.tos_digest() == kcode_k1_terms::sha256()
        && user.acceptance_signature() == identity.message_signature();

    if matches {
        Ok(())
    } else {
        Err("existing bootstrap Account conflicts with entered identity".to_owned())
    }
}

fn reconcile_group(groups: &K1Groups, user: UserId) -> Result<Group, String> {
    let mut matches = Vec::new();
    for id in groups.groups_for_user(user)? {
        if id.sentinel().is_none()
            && let Some(group) = groups.get(id)?
            && group.name().as_str() == GROUP_NAME
        {
            matches.push(group);
        }
    }

    let group = match matches.len() {
        0 => {
            let revision = groups.create(user, GroupName::new(GROUP_NAME.to_owned())?)?;
            groups
                .get(revision.group_id())?
                .ok_or_else(|| "created loom-devs group is unavailable".to_owned())?
        }
        1 => matches.pop().expect("one group"),
        _ => {
            return Err("multiple loom-devs groups are visible to the bootstrap user".to_owned());
        }
    };

    if !group
        .users()
        .iter()
        .any(|entry| entry.user_id() == user && entry.role() == GroupRole::Owner)
    {
        return Err("bootstrap user is not an Owner of loom-devs".to_owned());
    }

    Ok(group)
}

fn public_policy(group: GroupId) -> Result<AccessPolicy, String> {
    AccessPolicy::new(
        Authority::Group(group),
        Vec::new(),
        vec![
            ViewerSubject::Group(ALL_USERS),
            ViewerSubject::Group(ALL_MODELS),
        ],
    )
}

fn reconcile_profile(
    profiles: &K1AccessProfiles,
    user: UserId,
    group: GroupId,
    policy: &AccessPolicy,
) -> Result<ProfileId, String> {
    let mut exact = Vec::new();
    let mut conflicting = false;

    for profile in profiles.list_for_user(user)? {
        if profile.name().as_str() != PROFILE_NAME {
            continue;
        }

        let matches = profile.owner() == user
            && !profile.archived()
            && profile
                .color()
                .is_some_and(|color| color.as_str() == "amber")
            && profile.policy() == policy;

        if matches {
            exact.push(profile.profile_id());
        } else if profile.policy().authority() == Authority::Group(group) {
            conflicting = true;
        }
    }

    if conflicting || exact.len() > 1 {
        return Err("loom-devs Profile is ambiguous or conflicts with bootstrap policy".to_owned());
    }
    if let Some(profile) = exact.pop() {
        return Ok(profile);
    }

    let revision = profiles.create(
        user,
        ProfileName::new(PROFILE_NAME.to_owned())?,
        Some(ProfileColor::new("amber".to_owned())?),
        policy.clone(),
    )?;
    Ok(revision.profile_id())
}

fn reconcile_root(
    services: &BootstrapServices<'_>,
    context: &AccessContext,
    profile: ProfileId,
    policy: &AccessPolicy,
    group: GroupId,
) -> Result<AccessId, String> {
    let target = TargetId::new(
        Authority::Group(group),
        TargetName::new(LAUNCH_TARGET.to_owned())?,
    );

    let root = if services.launch_nodes.get(&target)?.is_some() {
        services.access_launch_nodes.lookup(context, &target)?
    } else {
        let revision = services.access_kmap.create_node(
            context,
            profile,
            policy.clone(),
            ROOT_TITLE.to_owned(),
            ROOT_HINT.to_owned(),
            ROOT_NARRATIVE.to_owned(),
            Vec::new(),
        )?;
        let root = revision.access_id();

        match services.access_launch_nodes.create(
            context,
            profile,
            policy.clone(),
            target.clone(),
            root,
        ) {
            Ok(()) => root,
            Err(error) => {
                let found = services
                    .access_launch_nodes
                    .lookup(context, &target)
                    .map_err(|_| error)?;
                if found != root {
                    return Err(
                        "loom-devs launch-node binding conflicts with newly created root"
                            .to_owned(),
                    );
                }
                root
            }
        }
    };

    let node = services.access_kmap.get_node(context, root)?;
    if node.title != ROOT_TITLE
        || node.navigation_hint != ROOT_HINT
        || node.narrative != ROOT_NARRATIVE
        || !node.connections.is_empty()
    {
        return Err("existing loom-devs Kmap root conflicts with bootstrap root".to_owned());
    }

    Ok(root)
}

fn verify_inventory(
    prepared: &[PreparedPackage],
    imported: &[kcode_k1_rust_bootstrap_import::ImportedPackage],
) -> Result<(), String> {
    if prepared.len() != imported.len()
        || !prepared.iter().zip(imported).all(|(left, right)| {
            left.logical_name() == right.logical_name()
                && left.version() == right.version()
                && left.source_sha256() == right.source_sha256()
        })
    {
        return Err("imported Rust package inventory differs from prepared closure".to_owned());
    }

    Ok(())
}