Project-Level Scope Resolution vs Global Scope Preferences in ai-memory: Key Differences Explained
Project-level scope resolution enforces strict data isolation by binding every database row and wiki page to a specific workspace-project tuple, while global scope preferences provide installation-wide defaults stored in a hidden project namespace that individual projects can override.
The ai-memory repository implements a dual-layer scoping system to separate data containment from configuration inheritance. Understanding how project-level scope resolution and global scope preferences interact ensures you maintain secure project boundaries while leveraging convenient default settings across your installation.
How Project-Level Scope Resolution Works
Project-level scope resolution guarantees that every row in the SQLite store, every wiki page, and every MCP request is bound to a specific workspace + project triple. This mechanism isolates data between projects and prevents accidental cross-project leakage.
The core type ScopeResolver in crates/ai-memory-core/src/routing_snippet.rs serves as the only entry point for looking up or creating a (workspace_id, project_id, path) tuple. All write-paths call helpers such as create_explicit_scope or resolve_many_existing_scopes, while read-paths use lookup_existing_scope and fail closed if the scope is missing. As documented in AGENTS.md, every SQL row carries this three-tuple, and any route touching the store must pass through the resolver, enabling the system to safely run multiple users or independent projects within the same binary.
Understanding Global Scope Preferences
Global scope preferences provide a mechanism for users or organizations to define persistent settings that apply across all projects unless explicitly overridden. These preferences include default auth tokens, LLM providers, and UI settings.
Global preferences are stored as durable wiki pages under a hidden "global" project whose ID remains constant for the entire installation. They are accessed via the memory API (specifically ai_memory::memory::get_global_pref or GlobalPrefs::get) and automatically merged into the configuration when Config::load() runs from crates/ai-memory-cli/src/config.rs. Because these preferences live in a dedicated global scope, they are not tied to any workspace or project data, ensuring that changing a global preference affects only default behaviors without compromising project data integrity.
Critical Differences in Implementation
Isolation Guarantees vs. Configuration Inheritance
Project-level scope resolution enforces mandatory isolation. Every request must supply an explicit scope, and there is no fallback to a global scope. If a scope cannot be resolved, the request fails with a 404-style error. This invariant is hard-coded in crates/ai-memory-core/src/routing_skills.rs.
Global scope preferences operate on an override cascade. The lookup logic first checks the project scope, then falls back to the global map. Individual projects can mask any global preference by storing a page with the same key under their own scope using Wiki::write_page from crates/ai-memory-wiki/src/wiki.rs.
Data Storage Locations
- Project data: Resides in scoped SQLite tables and wiki namespaces tied to the workspace-project tuple.
- Global preferences: Stored in the special "global" project namespace within the wiki system, accessible via
GlobalPrefsmethods.
Practical Code Examples
Resolving an Existing Project Scope
Use ScopeResolver to validate a project exists before performing operations:
use ai_memory_core::routing_skills::ScopeResolver;
use ai_memory_store::Scope;
// Returns an error if the scope does not exist
let resolver = ScopeResolver::new(&config)?;
let project_scope: Scope = resolver.lookup_existing_scope("my-workspace", "my-project")?;
Source: crates/ai-memory-core/src/routing_snippet.rs
Creating a New Project Scope
Initialize new project isolation boundaries explicitly:
let resolver = ScopeResolver::new(&config)?;
let new_scope = resolver.create_explicit_scope("my-workspace", "new-project")?;
Source: crates/ai-memory-core/src/routing_skills.rs
Reading Global Preferences
Access installation-wide defaults with fallback values:
use ai_memory_core::memory::GlobalPrefs;
// Falls back to "openai" if unset
let default_provider = GlobalPrefs::get("default_provider")
.unwrap_or_else(|| "openai".to_string());
Source: Implementation based on AGENTS.md specification
Overriding Global Preferences at Project Level
Mask global settings for specific project requirements:
// This masks the global setting for this project only
wiki.write_page(
&project_scope,
"settings/default_provider",
"anthropic"
)?;
Source: crates/ai-memory-wiki/src/wiki.rs
Summary
- Project-level scope resolution enforces strict data isolation through the
ScopeResolvertype inrouting_snippet.rs, requiring explicit workspace-project tuples for all data access with no fallback mechanisms. - Global scope preferences use a hidden "global" project namespace to store default configurations that cascade down to all projects unless overridden.
- The
ScopeResolverimplementation inrouting_skills.rsprovideslookup_existing_scopeandcreate_explicit_scopefor project management, whileGlobalPrefshandles cross-project configuration. - Configuration loading in
crates/ai-memory-cli/src/config.rsmerges global preferences automatically, but project-specific wiki pages always take precedence.
Frequently Asked Questions
Can global preferences leak sensitive data between projects?
No. Global preferences store only configuration defaults like provider names or UI themes, not project data. The ScopeResolver enforces that all actual data rows belong to specific project scopes. When a project queries data, the system exclusively uses the resolved workspace-project tuple, ensuring global configuration values never bypass project isolation boundaries.
What happens if a project doesn't define a specific preference?
The system performs a cascading lookup: it first checks for a value in the project's wiki namespace, then falls back to the global scope preferences stored in the hidden global project. If neither exists, the application uses hardcoded defaults or returns an error for mandatory configuration fields.
How do I migrate a setting from global to project-specific scope?
Write a wiki page to the target project scope using wiki.write_page(&project_scope, "settings/KEY", "VALUE") as implemented in crates/ai-memory-wiki/src/wiki.rs. Once stored, this value automatically masks the global preference for that specific project without affecting others or requiring changes to the global configuration.
Is ScopeResolver thread-safe for concurrent requests?
Yes. The ScopeResolver implementation in crates/ai-memory-core/src/routing_snippet.rs is designed to handle concurrent lookups across multiple threads. The SQLite store underlying the scope registry uses proper transaction isolation, and the resolver itself maintains no mutable state that would cause race conditions during lookup_existing_scope or resolve_many_existing_scopes calls.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →