# Public API
`K1Kmap` is the public facade.
```rust
use std::{path::Path, sync::Arc};
use kcode_k1_peering::K1Peering;
use kcode_k1_txn_ordering::K1TxnOrdering;
pub struct K1Kmap;
impl K1Kmap {
pub fn open(
root: &Path,
ordering: Arc<K1TxnOrdering>,
peering: Arc<K1Peering>,
) -> Result<Self, String>;
pub fn create_node(
&self,
title: impl Into<String>,
navigation_hint: impl Into<String>,
narrative: impl Into<String>,
connections: Vec<ConnectionSpec>,
) -> Result<TxId, String>;
pub fn update_node(
&self,
node_id: NodeId,
title: Option<String>,
navigation_hint: Option<String>,
narrative: Option<String>,
connection_changes: Vec<ConnectionChange>,
) -> Result<TxId, String>;
pub fn apply_measurements(
&self,
measurements: Vec<ConnectionMeasurement>,
) -> Result<TxId, String>;
pub fn get_node(&self, node_id: NodeId) -> Result<Option<Node>, String>;
pub fn open_node(
&self,
node_id: NodeId,
budget: f64,
temperature: f64,
mode: OpenMode,
access_filter: impl FnMut(NodeId) -> bool,
) -> Result<OpenResult, String>;
}
```
The loader exports are:
```rust
pub const PREVIEW_COST: f64 = 0.3;
pub const NARRATIVE_COST: f64 = 1.0;
pub const DEPTH_DECAY: f64 = 0.7;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OpenMode {
Full,
NavigationOnly,
}
#[derive(Clone, Debug, PartialEq)]
pub struct LoadedNode {
pub node_id: NodeId,
pub title: String,
pub navigation_hint: String,
pub narrative: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct OpenResult {
pub nodes: Vec<LoadedNode>,
pub automatic_attention_spent: f64,
}
```
The format exports are:
```rust
#[derive(Clone, Copy, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
pub enum ConnectionTier {
Navigation,
Automated,
}
#[derive(Clone, Copy, Debug, serde::Deserialize, PartialEq, serde::Serialize)]
pub struct Weight {
pub value: f64,
pub mass: f64,
}
impl Weight {
pub fn new(value: f64, mass: f64) -> Result<Self, KmapError>;
pub const fn initial() -> Self;
pub fn update(self, value: f64, mass: f64) -> Result<Option<Self>, KmapError>;
}
#[derive(Clone, Debug, serde::Deserialize, PartialEq, serde::Serialize)]
pub struct Connection {
pub target: NodeId,
pub tier: ConnectionTier,
pub weight: Weight,
}
impl Connection {
pub fn new(target: NodeId, tier: ConnectionTier) -> Self;
}
#[derive(Clone, Copy, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
pub struct ConnectionSpec {
pub target: NodeId,
pub tier: ConnectionTier,
}
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
pub enum ConnectionChange {
Set(ConnectionSpec),
Remove(NodeId),
}
#[derive(Clone, Debug, serde::Deserialize, PartialEq, serde::Serialize)]
pub struct ConnectionMeasurement {
pub source: NodeId,
pub target: NodeId,
pub value: f64,
pub mass: f64,
}
impl ConnectionMeasurement {
pub fn new(
source: NodeId,
target: NodeId,
value: f64,
mass: f64,
) -> Result<Self, KmapError>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MeasurementOutcome {
Updated,
Pruned,
Absent,
}
#[derive(Clone, Debug, serde::Deserialize, PartialEq, serde::Serialize)]
pub struct Node {
pub title: String,
pub navigation_hint: String,
pub narrative: String,
pub connections: Vec<Connection>,
}
impl Node {
pub fn new(
title: impl Into<String>,
navigation_hint: impl Into<String>,
narrative: impl Into<String>,
connections: Vec<Connection>,
) -> Result<Self, KmapError>;
pub fn from_specs(
title: impl Into<String>,
navigation_hint: impl Into<String>,
narrative: impl Into<String>,
specs: Vec<ConnectionSpec>,
) -> Result<Self, KmapError>;
pub fn validate(&self) -> Result<(), KmapError>;
pub fn apply_connection_changes(
&mut self,
changes: &[ConnectionChange],
) -> Result<(), KmapError>;
pub fn apply_measurement(
&mut self,
target: NodeId,
value: f64,
mass: f64,
) -> Result<MeasurementOutcome, KmapError>;
pub fn encode(&self) -> Result<Vec<u8>, KmapError>;
pub fn decode(bytes: &[u8]) -> Result<Self, KmapError>;
}
#[derive(Clone, Debug, serde::Deserialize, PartialEq, serde::Serialize)]
pub enum KmapAction {
CreateNode {
title: String,
navigation_hint: String,
narrative: String,
connections: Vec<ConnectionSpec>,
},
UpdateNode {
node_id: NodeId,
title: Option<String>,
navigation_hint: Option<String>,
narrative: Option<String>,
connection_changes: Vec<ConnectionChange>,
},
ApplyMeasurements {
measurements: Vec<ConnectionMeasurement>,
},
}
impl KmapAction {
pub fn create_node(
title: impl Into<String>,
navigation_hint: impl Into<String>,
narrative: impl Into<String>,
connections: Vec<ConnectionSpec>,
) -> Result<Self, KmapError>;
pub fn update_node(
node_id: NodeId,
title: Option<String>,
navigation_hint: Option<String>,
narrative: Option<String>,
connection_changes: Vec<ConnectionChange>,
) -> Result<Self, KmapError>;
pub fn apply_measurements(
measurements: Vec<ConnectionMeasurement>,
) -> Result<Self, KmapError>;
pub fn validate(&self) -> Result<(), KmapError>;
pub fn encode(&self) -> Result<Vec<u8>, KmapError>;
pub fn decode(bytes: &[u8]) -> Result<Self, KmapError>;
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KmapError(pub String);
#[derive(Clone, Copy, Debug, serde::Deserialize, Eq, Hash, PartialEq, serde::Serialize)]
pub struct NodeId(pub [u8; 12]);
impl From<TxId> for NodeId;
impl From<NodeId> for TxId;
```
`KmapError` also implements `std::fmt::Display` and `std::error::Error`.
The transaction ID export is:
```rust
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TxId(/* private fields */);
impl TxId {
pub const fn from_bytes(bytes: [u8; 12]) -> Self;
pub const fn into_bytes(self) -> [u8; 12];
pub const fn as_bytes(&self) -> &[u8; 12];
pub fn for_transaction(transaction: &[u8]) -> Self;
pub fn verify(&self, transaction: &[u8]) -> bool;
}
impl std::str::FromStr for TxId {
type Err = kcode_k1_transaction_id::ParseTxIdError;
fn from_str(value: &str) -> Result<Self, Self::Err>;
}
```
`TxId` also implements `std::fmt::Display` and `std::fmt::Debug`. Its parse-error type is the associated type above and is not reexported by this facade.
## Semantics
Each facade mutation encodes one action and submits it once through `K1Peering`; projection state changes only through ordering callbacks. `Applied`, `Unchanged`, and `Rejected` callback outcomes are successful. A malformed callback payload or projection infrastructure failure faults instance availability. A reorganization faults the instance and then clears the derived projection. Successfully opening a fresh instance is the recovery path. Projection read failures fault availability and can fail both `get_node` and traversal.
Connection targets in a `Node` are unique, with at most 12 `Navigation` connections. Weight and measurement values must be finite and in `[0, 1]`, and masses must be finite and positive. New connections use `Weight { value: 1.0, mass: 3.0 }`; updates decay prior mass, return the weighted result, and return `None` below the pruning threshold. Connection changes and measurements are atomic on validation failure, tier changes preserve weight, and a measurement for an absent target returns `Absent`.
`Node` and `KmapAction` encoding is deterministic, versioned, and validated; decoding rejects malformed, unsupported, trailing, and invariant-invalid values. Their methods, rather than direct serialization of public fields, define the wire format. `NodeId` and `TxId` convert losslessly in both directions.
The caller must authorize the root passed to `open_node`; `access_filter` governs target access. Budget and temperature must be finite and nonnegative.
In `Full` mode the root narrative and accessible root `Navigation` previews are free. The first selected hit on an unseen target costs `PREVIEW_COST`; its next selected hit costs `NARRATIVE_COST` plus newly guaranteed `Navigation` previews. Selection only admits candidates whose required cost is affordable.
`NavigationOnly` returns no narratives. It loads each selected node and its unseen accessible `Navigation` children as one atomic preview bundle, and loaded `Navigation` children push their outgoing tickets one extra depth. The first selected unaffordable bundle stops traversal globally without partial output or resampling.
All operations are synchronous and have no finite wall-clock bound because callback work, storage work, and the caller's access predicate may block.