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}