Skip to main content

ijima_core/
capabilities.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4//! Ijima's capability vocabulary.
5//!
6//! These stable wire identifiers map onto Schubert's capability model.
7//! The geometric policy — Grassmannian, partitions, capability kinds, and
8//! principal grants — lives in
9//! [`policy/policy.toml`](../../policy/policy.toml) at the repository root
10//! and is loaded by [`ijima_server::auth`] via Schubert's `policy`
11//! feature.
12//!
13//! ## Policy selection (via Schubert's recommender)
14//!
15//! Ijima's access-control constraints were fed to Schubert's
16//! `recommend` CLI (5 roles, 3 namespaces, audit + crypto + policy
17//! required, discrete trust, ~50 principals). It selected:
18//!
19//! - **Grassmannian Gr(4,8)**, policy dimension `k(n-k) = 16`
20//!   (Schubert's enterprise / multi-tenant bucket).
21//! - **Features**: `std`, `crypto`, `policy`.
22//! - **Computation path**: LR.
23//!
24//! The `policy` feature means the vocabulary is declarative TOML, not
25//! hardcoded Rust — see `policy/policy.toml`.
26//!
27//! ## Vocabulary (on Gr(4,8), partitions fit a 4×4 box)
28//!
29//! | Capability ID | Kind | Partition | Codim | Grants |
30//! |---|---|---|---|---|
31//! | [`MEMORY_READ`] | ReadLike | σ₁ | 1 | read memory palace entries |
32//! | [`KNOWLEDGE_READ`] | ReadLike | σ₁ | 1 | query entities/triples/timeline |
33//! | [`MINING_REVIEW`] | ReadLike | σ₂ | 2 | read + accept/reject the review queue |
34//! | [`MEMORY_WRITE`] | WriteLike | σ₂ | 2 | store palace entries (dedup-aware) |
35//! | [`KNOWLEDGE_WRITE`] | WriteLike | σ₂ | 2 | add/invalidate triples |
36//! | [`SESSION_INGEST`] | WriteLike | σ₃ | 3 | append session-context turns |
37//! | [`MINING_TRIGGER`] | WriteLike | σ₃₁ | 4 | trigger an extraction pass |
38//! | [`TRUST_PROMOTE`] | WriteLike | σ₃₁ | 4 | promote content to a higher trust tier / shared namespace |
39//! | [`TRUST_ENDORSE`] | WriteLike | σ₃₂ | 5 | endorse mined/auto content as Explicit |
40//! | [`TRUST_OVERRIDE`] | WriteLike | σ₄₂ | 6 | override local authority (Phase 5) |
41//! | [`ADMIN`] | AdminLike | σ₄₄₄₄ (point) | 16 | full control |
42
43/// The Grassmannian Ijima's policy lives on: **Gr(4,8)**, dimension 16.
44/// Selected by Schubert's recommender for Ijima's multi-tenant
45/// (3-namespace, 5-role) constraint set.
46pub const POLICY_GRASSMANNIAN: (usize, usize) = (4, 8);
47
48/// Read memory palace entries.
49pub const MEMORY_READ: &str = "memory:read";
50/// Query entities, triples, and the knowledge-graph timeline.
51pub const KNOWLEDGE_READ: &str = "knowledge:read";
52/// Read and accept/reject the mining review queue.
53pub const MINING_REVIEW: &str = "mining:review";
54/// Store palace entries (dedup-aware).
55pub const MEMORY_WRITE: &str = "memory:write";
56/// Add or invalidate knowledge-graph triples.
57pub const KNOWLEDGE_WRITE: &str = "knowledge:write";
58/// Append raw session-context turns to the repository.
59pub const SESSION_INGEST: &str = "session:ingest";
60/// Trigger a mining/extraction pass over session context.
61pub const MINING_TRIGGER: &str = "mining:trigger";
62/// Full administrative control (the point class σ₄₄₄₄; implies all others).
63pub const ADMIN: &str = "admin";
64
65// --- Trust-tier transitions (ADR: provenance-tier model) ---
66// Raising trust is costlier than writing at a tier, so these sit at higher
67// codimension than `memory:write`. `trust:override` is default-deny in
68// policy (no principal seeded with it) — wired in Phase 5.
69
70/// Promote content to a higher trust tier / shared namespace. Replaces the
71/// plain `memory:write` check on `promote_memory` (codim 4, a consequential
72/// write on par with `mining:trigger`).
73pub const TRUST_PROMOTE: &str = "trust:promote";
74/// Endorse mined/auto content as Explicit — a cross-tier jump (codim 5).
75pub const TRUST_ENDORSE: &str = "trust:endorse";
76/// Override local authority (accept conflicting content) — Phase 5 (codim 6).
77pub const TRUST_OVERRIDE: &str = "trust:override";
78
79/// Every capability wire ID, in increasing-codimension order. Used to
80/// validate identifiers at the API boundary; the geometric definitions
81/// live in `policy/policy.toml`.
82pub const ALL_CAPABILITIES: &[&str] = &[
83    MEMORY_READ,
84    KNOWLEDGE_READ,
85    MINING_REVIEW,
86    MEMORY_WRITE,
87    KNOWLEDGE_WRITE,
88    SESSION_INGEST,
89    MINING_TRIGGER,
90    TRUST_PROMOTE,
91    TRUST_ENDORSE,
92    TRUST_OVERRIDE,
93    ADMIN,
94];
95
96/// The Schubert intersection number (codimension) of a capability's
97/// partition — the geometric weight used for rate-limiting capacity.
98///
99/// A capability's codimension is the sum of its partition parts (the
100/// degree of its Schubert cycle). Per Schubert's rate-limiter, this
101/// becomes the per-principal token-bucket capacity multiplier: a
102/// principal holding `memory:write` (codim 2) gets 2× the throughput of
103/// one holding `memory:read` (codim 1); `admin` (the point class, codim
104/// 16) gets 16×. *The geometry of access maps to the geometry of
105/// throughput.*
106///
107/// **Coupling:** these codimensions mirror `policy/policy.toml`. If the
108/// policy's partitions change, update this mapping. Unknown capability
109/// ids default to codim 1 (the lowest, σ₁) so a future capability is
110/// rate-limited conservatively until promoted here.
111pub fn intersection_number(capability: &str) -> u64 {
112    match capability {
113        MEMORY_READ | KNOWLEDGE_READ => 1,
114        MINING_REVIEW | MEMORY_WRITE | KNOWLEDGE_WRITE => 2,
115        SESSION_INGEST => 3,
116        MINING_TRIGGER | TRUST_PROMOTE => 4,
117        TRUST_ENDORSE => 5,
118        TRUST_OVERRIDE => 6,
119        ADMIN => 16,
120        _ => 1,
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use std::collections::HashSet;
128
129    #[test]
130    fn all_capability_ids_are_unique() {
131        let set: HashSet<&&str> = ALL_CAPABILITIES.iter().collect();
132        assert_eq!(set.len(), ALL_CAPABILITIES.len(), "duplicate capability id");
133    }
134
135    #[test]
136    fn policy_grassmannian_is_valid_schubert_space() {
137        let (k, n) = POLICY_GRASSMANNIAN;
138        // Schubert requires 0 < k < n.
139        assert!(k > 0 && k < n);
140        // Must match the recommender-selected enterprise/multi-tenant preset.
141        assert_eq!(k * (n - k), 16);
142    }
143
144    #[test]
145    fn admin_is_in_vocabulary() {
146        assert!(ALL_CAPABILITIES.contains(&ADMIN));
147    }
148
149    #[test]
150    fn intersection_numbers_match_policy_codimensions() {
151        // Codimension = sum of partition parts (mirrors policy/policy.toml).
152        assert_eq!(intersection_number(MEMORY_READ), 1);
153        assert_eq!(intersection_number(KNOWLEDGE_READ), 1);
154        assert_eq!(intersection_number(MINING_REVIEW), 2);
155        assert_eq!(intersection_number(MEMORY_WRITE), 2);
156        assert_eq!(intersection_number(KNOWLEDGE_WRITE), 2);
157        assert_eq!(intersection_number(SESSION_INGEST), 3);
158        assert_eq!(intersection_number(MINING_TRIGGER), 4);
159        assert_eq!(intersection_number(TRUST_PROMOTE), 4);
160        assert_eq!(intersection_number(TRUST_ENDORSE), 5);
161        assert_eq!(intersection_number(TRUST_OVERRIDE), 6);
162        assert_eq!(intersection_number(ADMIN), 16);
163        // Unknown capabilities default to the lowest codim (conservative).
164        assert_eq!(intersection_number("future:cap"), 1);
165    }
166}