Skip to main content

ijima_core/
namespace.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Memory namespaces — the isolation boundary for multi-user access (D2).
5//!
6//! Every read and write through the [`crate::Store`] is scoped to a
7//! [`NamespaceId`]. A namespace is the unit of visibility: an operator's
8//! private memory, a shared project namespace, or the global commons
9//! (the legacy pi-mempalace "everyone sees everything" mode).
10
11/// Identifies a memory namespace.
12///
13/// Wire form is a stable opaque string (e.g. `ns_elliott_private`,
14/// `ns_ijima_shared`, `ns_global`). v0 stores this as a SurrealDB record
15/// field; a future promotion maps each namespace onto a native SurrealDB
16/// `NS`/`DB` scope for first-class isolation.
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "serde", serde(transparent))]
20pub struct NamespaceId(pub String);
21
22impl NamespaceId {
23    /// Construct a namespace id from any string-like value.
24    pub fn new(id: impl Into<String>) -> Self {
25        Self(id.into())
26    }
27
28    /// The stable wire string.
29    pub fn as_str(&self) -> &str {
30        &self.0
31    }
32}
33
34/// The visibility/isolation class of a namespace.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub enum NamespaceKind {
38    /// One operator's personal memory — visible only to that operator.
39    Private,
40    /// A project/team namespace — visible to a configured group.
41    Shared,
42    /// The global commons — visible to everyone (legacy pi-mempalace mode).
43    Global,
44}
45
46/// A namespace descriptor: its id, visibility class, and owning operator.
47#[derive(Debug, Clone, PartialEq, Eq)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49pub struct Namespace {
50    /// Stable opaque identifier.
51    pub id: NamespaceId,
52    /// Visibility class.
53    pub kind: NamespaceKind,
54    /// Owning operator principal id (`"system"` for the global commons).
55    pub owner: String,
56}
57
58/// The global doctrine namespace — curated, Git-versioned memories
59/// ([`crate::MemorySource::Doctrine`]) mirrored from the seed pack.
60/// Readable by every principal via `?namespace=ns_doctrine`.
61pub const DOCTRINE_NAMESPACE: &str = "ns_doctrine";
62
63/// A principal's membership in a shared namespace — the WS3 org-wall
64/// grant. Membership is store-backed (mutable at runtime, no redeploy);
65/// see the namespace-membership ADR.
66#[derive(Debug, Clone, PartialEq, Eq)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68pub struct NamespaceMembership {
69    /// The shared namespace (`ns_<org>_shared`).
70    pub namespace: String,
71    /// The principal granted access.
72    pub principal: String,
73    /// Unix seconds when the grant was recorded.
74    pub granted_at_unix: u64,
75    /// The admin principal who issued the grant (audit trail).
76    pub granted_by: String,
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn namespace_id_round_trips() {
85        let ns = NamespaceId::new("ns_elliott_private");
86        assert_eq!(ns.as_str(), "ns_elliott_private");
87        assert_eq!(ns, NamespaceId("ns_elliott_private".into()));
88    }
89
90    #[test]
91    fn global_namespace_owner_is_system() {
92        let global = Namespace {
93            id: NamespaceId::new("ns_global"),
94            kind: NamespaceKind::Global,
95            owner: "system".into(),
96        };
97        assert_eq!(global.kind, NamespaceKind::Global);
98        assert_eq!(global.owner, "system");
99    }
100}