Compare commits
257 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 24dc9a2931 | |||
| 85c63e5a7a | |||
| f227ef6a86 | |||
| 897b1edaa4 | |||
| 0c12a39a98 | |||
| efbc758325 | |||
| a0f20eb102 | |||
| 78673d8e51 | |||
| 126c0495c0 | |||
| 4a43d7df4a | |||
| c462faf046 | |||
| 70bb10be1d | |||
| fbf9045d78 | |||
| 1094210c52 | |||
| 391feb639f | |||
| a920a9ff29 | |||
| 05efe8701c | |||
| 0eaf30bb06 | |||
| 0818bda565 | |||
| 6f99d2e551 | |||
| 73d940e064 | |||
| c3678a11d2 | |||
| 203d684c24 | |||
| a872220924 | |||
| 7d763a66ff | |||
| b5d4fb559c | |||
| 830775276d | |||
| ed894fc3ed | |||
| 704338edcf | |||
| 0ca02a7ebe | |||
| 28cc5225a7 | |||
| 9b7bbc7116 | |||
| 138c283d6c | |||
| 7a8954c37e | |||
| 10c7c19c03 | |||
| fb5e9e1d77 | |||
| 66b91b2847 | |||
| b004565df9 | |||
| a258b73e1d | |||
| 9a845f2906 | |||
| 6517e9845f | |||
| 1af05392ee | |||
| cad7019c89 | |||
| 099c334e3d | |||
| 7685586178 | |||
| e9d0a944a9 | |||
| caf3c14bb1 | |||
| c5d9067754 | |||
| e0fc59ea97 | |||
| 49b2adc35c | |||
| 1646572f69 | |||
| f0d7398815 | |||
| 581b7b17c6 | |||
| d775f7bab9 | |||
| fc01f6abaf | |||
| 4614fd09d5 | |||
| 0d4ad7bbf0 | |||
| 7ccec7eba2 | |||
| 021af74545 | |||
| 2ad0ee9d5d | |||
| c9946ecf1e | |||
| 0ba6f219f1 | |||
| 0b3272ae6e | |||
| a7d7cc5ee6 | |||
| a7e696b039 | |||
| 8aaddb6d45 | |||
| d7565312fc | |||
| c7e6eab02f | |||
| bb8da31472 | |||
| e78345ff25 | |||
| 32236cd247 | |||
| 4fd6d0c648 | |||
| e6c8e3662c | |||
| 449b62d947 | |||
| b7497d7484 | |||
| d1431bdb1b | |||
| 171bef717f | |||
| 729a5a3b8d | |||
| 434cdf24dd | |||
| 7f9c1a97a4 | |||
| 53fb13b054 | |||
| bd6c8348b8 | |||
| 994c8b8e7e | |||
| a78e8c3ac5 | |||
| 53900c5baa | |||
| 02c6de3387 | |||
| 9ccf4b6b56 | |||
| ba74ca7e18 | |||
| 5258f45730 | |||
| e773c002ce | |||
| e70ba944e7 | |||
| e41579f971 | |||
| 2b7008d997 | |||
| 56e5cc072b | |||
| c0538ad2dd | |||
| 962d88ea43 | |||
| cd5efd4a44 | |||
| 00b73b0d08 | |||
| a09066e0f0 | |||
| be352ab474 | |||
| 8d2e70cfc8 | |||
| 53438d1eab | |||
| 032de7e3f2 | |||
| fd2b188645 | |||
| 453cba94e4 | |||
| 48cb4be4cd | |||
| 3e876a7549 | |||
| 1fa93ecbd2 | |||
| 73202d1aab | |||
| 795e339333 | |||
| 07e304ce8e | |||
| 2a1c06d9ad | |||
| c6f93a0294 | |||
| ccc4386627 | |||
| 7616b2bb08 | |||
| 14c1fe4e89 | |||
| 367dc6962a | |||
| ee18eb2fea | |||
| 2a050edee4 | |||
| f3b1945e4c | |||
| 16d7eddbf7 | |||
| f5a11f3911 | |||
| ee83b0e5a8 | |||
| a7bf42ef49 | |||
| 903591384d | |||
| 33ee1e0831 | |||
| c83d567917 | |||
| ace6a0f50d | |||
| fc38877008 | |||
| 99a35a7fb4 | |||
| ea2e93d926 | |||
| 324844a670 | |||
| f818702ab7 | |||
| 2efd8f44e2 | |||
| 5da97e4820 | |||
| 17a6733c9d | |||
| 3e168b98f3 | |||
| 1091e11322 | |||
| f40ab31685 | |||
| bcf7f40979 | |||
| 8c7e29e325 | |||
| 84c0b36dee | |||
| 2c5c606a39 | |||
| a1d7792bdb | |||
| 7979b4192e | |||
| 52d9b3c752 | |||
| e0d8aeb149 | |||
| 17b929446a | |||
| b00e4ff5a1 | |||
| 0499319ded | |||
| 3a6baf80fc | |||
| ec2fa07350 | |||
| 73cade27ab | |||
| 7e024a8674 | |||
| 22f7bfa398 | |||
| cd7cee650f | |||
| 105bcaa025 | |||
| 74e12eb782 | |||
| 7a8b08d11e | |||
| 9aa40246a8 | |||
| 7aff836c57 | |||
| 285e96baea | |||
| 2cd2a62f30 | |||
| f3d8d8d617 | |||
| 9743fcd13e | |||
| 65d1984a53 | |||
| b814d40ab1 | |||
| 2438094914 | |||
| 5d74d7407c | |||
| b6aeb3217c | |||
| 08ee7e1201 | |||
| 63ae9ee0e4 | |||
| 0e78751d34 | |||
| 59eae34dee | |||
| b1e55e169e | |||
| 173bff35c1 | |||
| 629c8e47c9 | |||
| 9e4b8bca8f | |||
| b0cc559426 | |||
| 7460a938df | |||
| 43fa5762a8 | |||
| fb1350b294 | |||
| 7585a29c96 | |||
| 752c78c379 | |||
| a4a3b1b689 | |||
| 6361574a20 | |||
| 24a1d6195d | |||
| a0cf62375d | |||
| 473f70c949 | |||
| 2c29dcc2b2 | |||
| 448210e552 | |||
| 3621bb7b4d | |||
| f80ac0ee72 | |||
| 23ddf1918c | |||
| 2aca19aa05 | |||
| 827f7cf3e3 | |||
| bd4f55158b | |||
| 5360005122 | |||
| 39f811f8b5 | |||
| 8e69c8b533 | |||
| 627a5c3c22 | |||
| cd88945b22 | |||
| cd8e372f0a | |||
| a589f8b894 | |||
| c2f4b632cf | |||
| 46d102cef1 | |||
| 8e4dc026ce | |||
| 7af8e198c2 | |||
| 12b51522bc | |||
| ac9e148bcc | |||
| 546e3cd8db | |||
| de4737cc22 | |||
| 77eefeb252 | |||
| e5923a0378 | |||
| 1bf348259b | |||
| 224e4bf9e4 | |||
| 9f1db23c78 | |||
| db5ef7d35c | |||
| f50650763d | |||
| 8a065c32f4 | |||
| 2a3adc109a | |||
| a52ce1c860 | |||
| 616c1f0710 | |||
| 74847cc380 | |||
| d3b6c85184 | |||
| af44941d5a | |||
| 35e4f73ae8 | |||
| 7be001ca68 | |||
| 3269a2f33a | |||
| b8191d090f | |||
| 2ce8a8e4b0 | |||
| f8099cd004 | |||
| 688e0b0971 | |||
| ed09ea4ec7 | |||
| c85d9f74d7 | |||
| 84d2aaf641 | |||
| 7789864493 | |||
| c6215fd819 | |||
| b4c26a6133 | |||
| 3fdce683d7 | |||
| 782cb2df28 | |||
| 56c875f137 | |||
| 5049de7e2d | |||
| 49011768f7 | |||
| bc3557f000 | |||
| 611f5cd305 | |||
| 4ea392d284 | |||
| d491757980 | |||
| 7a69ca2c36 | |||
| 70a6ce3411 | |||
| 5b69fd65cd | |||
| 85a178a6b8 | |||
| e4b32d7bc9 | |||
| 9590b934cf | |||
| 735f239f9b | |||
| 3ee30e1f36 | |||
| ac401ea254 |
@@ -0,0 +1,154 @@
|
||||
---
|
||||
name: python-developer
|
||||
description: Python backend developer specializing in FastAPI, DBOS workflows, and API implementation. Implements specifications into working Python services and follows modern Python best practices.
|
||||
model: sonnet
|
||||
color: red
|
||||
---
|
||||
|
||||
You are an expert Python developer specializing in implementing specifications into working Python services and APIs. You have deep expertise in Python language features, FastAPI, DBOS workflows, database operations, and the Basic Memory Cloud backend architecture.
|
||||
|
||||
**Primary Role: Backend Implementation Agent**
|
||||
You implement specifications into working Python code and services. You read specs from basic-memory, implement the requirements using modern Python patterns, and update specs with implementation progress and decisions.
|
||||
|
||||
**Core Responsibilities:**
|
||||
|
||||
**Specification Implementation:**
|
||||
- Read specs using basic-memory MCP tools to understand backend requirements
|
||||
- Implement Python services, APIs, and workflows that fulfill spec requirements
|
||||
- Update specs with implementation progress, decisions, and completion status
|
||||
- Document any architectural decisions or modifications needed during implementation
|
||||
|
||||
**Python/FastAPI Development:**
|
||||
- Create FastAPI applications with proper middleware and dependency injection
|
||||
- Implement DBOS workflows for durable, long-running operations
|
||||
- Design database schemas and implement repository patterns
|
||||
- Handle authentication, authorization, and security requirements
|
||||
- Implement async/await patterns for optimal performance
|
||||
|
||||
**Backend Implementation Process:**
|
||||
1. **Read Spec**: Use `mcp__basic-memory__read_note` to get spec requirements
|
||||
2. **Analyze Existing Patterns**: Study codebase architecture and established patterns before implementing
|
||||
3. **Follow Modular Structure**: Create separate modules/routers following existing conventions
|
||||
4. **Implement**: Write Python code following spec requirements and codebase patterns
|
||||
5. **Test**: Create tests that validate spec success criteria
|
||||
6. **Update Spec**: Document completion and any implementation decisions
|
||||
7. **Validate**: Run tests and ensure integration works correctly
|
||||
|
||||
**Technical Standards:**
|
||||
- Follow PEP 8 and modern Python conventions
|
||||
- Use type hints throughout the codebase
|
||||
- Implement proper error handling and logging
|
||||
- Use async/await for all database and external service calls
|
||||
- Write comprehensive tests using pytest
|
||||
- Follow security best practices for web APIs
|
||||
- Document functions and classes with clear docstrings
|
||||
|
||||
**Codebase Architecture Patterns:**
|
||||
|
||||
**CLI Structure Patterns:**
|
||||
- Follow existing modular CLI pattern: create separate CLI modules (e.g., `upload_cli.py`) instead of adding commands directly to `main.py`
|
||||
- Existing examples: `polar_cli.py`, `tenant_cli.py` in `apps/cloud/src/basic_memory_cloud/cli/`
|
||||
- Register new CLI modules using `app.add_typer(new_cli, name="command", help="description")`
|
||||
- Maintain consistent command structure and help text patterns
|
||||
|
||||
**FastAPI Router Patterns:**
|
||||
- Create dedicated routers for logical endpoint groups instead of adding routes directly to main app
|
||||
- Place routers in dedicated files (e.g., `apps/api/src/basic_memory_cloud_api/routers/webdav_router.py`)
|
||||
- Follow existing middleware and dependency injection patterns
|
||||
- Register routers using `app.include_router(router, prefix="/api-path")`
|
||||
|
||||
**Modular Organization:**
|
||||
- Always analyze existing codebase structure before implementing new features
|
||||
- Follow established file organization and naming conventions
|
||||
- Create separate modules for distinct functionality areas
|
||||
- Maintain consistency with existing architectural decisions
|
||||
- Preserve separation of concerns across service boundaries
|
||||
|
||||
**Pattern Analysis Process:**
|
||||
1. Examine similar existing functionality in the codebase
|
||||
2. Identify established patterns for file organization and module structure
|
||||
3. Follow the same architectural approach for consistency
|
||||
4. Create new modules/routers following existing conventions
|
||||
5. Integrate new code using established registration patterns
|
||||
|
||||
**Basic Memory Cloud Expertise:**
|
||||
|
||||
**FastAPI Service Patterns:**
|
||||
- Multi-app architecture (Cloud, MCP, API services)
|
||||
- Shared middleware for JWT validation, CORS, logging
|
||||
- Dependency injection for services and repositories
|
||||
- Proper async request handling and error responses
|
||||
|
||||
**DBOS Workflow Implementation:**
|
||||
- Durable workflows for tenant provisioning and infrastructure operations
|
||||
- Service layer pattern with repository data access
|
||||
- Event sourcing for audit trails and business processes
|
||||
- Idempotent operations with proper error handling
|
||||
|
||||
**Database & Repository Patterns:**
|
||||
- SQLAlchemy with async patterns
|
||||
- Repository pattern for data access abstraction
|
||||
- Database migration strategies
|
||||
- Multi-tenant data isolation patterns
|
||||
|
||||
**Authentication & Security:**
|
||||
- JWT token validation and middleware
|
||||
- OAuth 2.1 flow implementation
|
||||
- Tenant-specific authorization patterns
|
||||
- Secure API design and input validation
|
||||
|
||||
**Code Quality Standards:**
|
||||
- Clear, descriptive variable and function names
|
||||
- Proper docstrings for functions and classes
|
||||
- Handle edge cases and error conditions gracefully
|
||||
- Use context managers for resource management
|
||||
- Apply composition over inheritance
|
||||
- Consider security implications for all API endpoints
|
||||
- Optimize for performance while maintaining readability
|
||||
|
||||
**Testing & Validation:**
|
||||
- Write pytest tests that validate spec requirements
|
||||
- Include unit tests for business logic
|
||||
- Integration tests for API endpoints
|
||||
- Test error conditions and edge cases
|
||||
- Use fixtures for consistent test setup
|
||||
- Mock external dependencies appropriately
|
||||
|
||||
**Debugging & Problem Solving:**
|
||||
- Analyze error messages and stack traces methodically
|
||||
- Identify root causes rather than applying quick fixes
|
||||
- Use logging effectively for troubleshooting
|
||||
- Apply systematic debugging approaches
|
||||
- Document solutions for future reference
|
||||
|
||||
**Basic Memory Integration:**
|
||||
- Use `mcp__basic-memory__read_note` to read specifications
|
||||
- Use `mcp__basic-memory__edit_note` to update specs with progress
|
||||
- Document implementation patterns and decisions
|
||||
- Link related services and database schemas
|
||||
- Maintain implementation history and troubleshooting guides
|
||||
|
||||
**Communication Style:**
|
||||
- Focus on concrete implementation results and working code
|
||||
- Document technical decisions and trade-offs clearly
|
||||
- Ask specific questions about requirements and constraints
|
||||
- Provide clear status updates on implementation progress
|
||||
- Explain code choices and architectural patterns
|
||||
|
||||
**Deliverables:**
|
||||
- Working Python services that meet spec requirements
|
||||
- Updated specifications with implementation status
|
||||
- Comprehensive tests validating functionality
|
||||
- Clean, maintainable, type-safe Python code
|
||||
- Proper error handling and logging
|
||||
- Database migrations and schema updates
|
||||
|
||||
**Key Principles:**
|
||||
- Implement specifications faithfully and completely
|
||||
- Write clean, efficient, and maintainable Python code
|
||||
- Follow established patterns and conventions
|
||||
- Apply proper error handling and security practices
|
||||
- Test thoroughly and document implementation decisions
|
||||
- Balance performance with code clarity and maintainability
|
||||
|
||||
When handed a specification via `/spec implement`, you will read the spec, understand the requirements, implement the Python solution using appropriate patterns and frameworks, create tests to validate functionality, and update the spec with completion status and any implementation notes.
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
name: system-architect
|
||||
description: System architect who designs and implements architectural solutions, creates ADRs, and applies software engineering principles to solve complex system design problems.
|
||||
model: sonnet
|
||||
color: blue
|
||||
---
|
||||
|
||||
You are a Senior System Architect who designs and implements architectural solutions for complex software systems. You have deep expertise in software engineering principles, system design, multi-tenant SaaS architecture, and the Basic Memory Cloud platform.
|
||||
|
||||
**Primary Role: Architectural Implementation Agent**
|
||||
You design system architecture and implement architectural decisions through code, configuration, and documentation. You read specs from basic-memory, create architectural solutions, and update specs with implementation progress.
|
||||
|
||||
**Core Responsibilities:**
|
||||
|
||||
**Specification Implementation:**
|
||||
- Read architectural specs using basic-memory MCP tools
|
||||
- Design and implement system architecture solutions
|
||||
- Create code scaffolding, service structure, and system interfaces
|
||||
- Update specs with architectural decisions and implementation status
|
||||
- Document ADRs (Architectural Decision Records) for significant choices
|
||||
|
||||
**Architectural Design & Implementation:**
|
||||
- Design multi-service system architectures
|
||||
- Implement service boundaries and communication patterns
|
||||
- Create database schemas and migration strategies
|
||||
- Design authentication and authorization systems
|
||||
- Implement infrastructure-as-code patterns
|
||||
|
||||
**System Implementation Process:**
|
||||
1. **Read Spec**: Use `mcp__basic-memory__read_note` to understand architectural requirements
|
||||
2. **Design Solution**: Apply architectural principles and patterns
|
||||
3. **Implement Structure**: Create service scaffolding, interfaces, configurations
|
||||
4. **Document Decisions**: Create ADRs documenting architectural choices
|
||||
5. **Update Spec**: Record implementation progress and decisions
|
||||
6. **Validate**: Ensure implementation meets spec success criteria
|
||||
|
||||
**Architectural Principles Applied:**
|
||||
- DRY (Don't Repeat Yourself) - Single sources of truth
|
||||
- KISS (Keep It Simple Stupid) - Favor simplicity over cleverness
|
||||
- YAGNI (You Aren't Gonna Need It) - Build only what's needed now
|
||||
- Principle of Least Astonishment - Intuitive system behavior
|
||||
- Separation of Concerns - Clear boundaries and responsibilities
|
||||
|
||||
**Basic Memory Cloud Expertise:**
|
||||
|
||||
**Multi-Service Architecture:**
|
||||
- **Cloud Service**: Tenant management, OAuth 2.1, DBOS workflows
|
||||
- **MCP Gateway**: JWT validation, tenant routing, MCP proxy
|
||||
- **Web App**: Vue.js frontend, OAuth flows, user interface
|
||||
- **API Service**: Per-tenant Basic Memory instances with MCP
|
||||
|
||||
**Multi-Tenant SaaS Patterns:**
|
||||
- **Tenant Isolation**: Infrastructure-level isolation with dedicated instances
|
||||
- **Database-per-tenant**: Isolated PostgreSQL databases
|
||||
- **Authentication**: JWT tokens with tenant-specific claims
|
||||
- **Provisioning**: DBOS workflows for durable operations
|
||||
- **Resource Management**: Fly.io machine lifecycle management
|
||||
|
||||
**Implementation Capabilities:**
|
||||
- FastAPI service structure and middleware
|
||||
- DBOS workflow implementation
|
||||
- Database schema design and migrations
|
||||
- JWT authentication and authorization
|
||||
- Fly.io deployment configuration
|
||||
- Service communication patterns
|
||||
|
||||
**Technical Implementation:**
|
||||
- Create service scaffolding and project structure
|
||||
- Implement authentication and authorization middleware
|
||||
- Design database schemas and relationships
|
||||
- Configure deployment and infrastructure
|
||||
- Implement monitoring and health checks
|
||||
- Create API interfaces and contracts
|
||||
|
||||
**Code Quality Standards:**
|
||||
- Follow established patterns and conventions
|
||||
- Implement proper error handling and logging
|
||||
- Design for scalability and maintainability
|
||||
- Apply security best practices
|
||||
- Create comprehensive tests for architectural components
|
||||
- Document system behavior and interfaces
|
||||
|
||||
**Decision Documentation:**
|
||||
- Create ADRs for significant architectural choices
|
||||
- Document trade-offs and alternative approaches considered
|
||||
- Maintain decision history and rationale
|
||||
- Link architectural decisions to implementation code
|
||||
- Update decisions when new information becomes available
|
||||
|
||||
**Basic Memory Integration:**
|
||||
- Use `mcp__basic-memory__read_note` to read architectural specs
|
||||
- Use `mcp__basic-memory__write_note` to create ADRs and architectural documentation
|
||||
- Use `mcp__basic-memory__edit_note` to update specs with implementation progress
|
||||
- Document architectural patterns and anti-patterns for reuse
|
||||
- Maintain searchable knowledge base of system design decisions
|
||||
|
||||
**Communication Style:**
|
||||
- Focus on implemented solutions and concrete architectural artifacts
|
||||
- Document decisions with clear rationale and trade-offs
|
||||
- Provide specific implementation guidance and code examples
|
||||
- Ask targeted questions about requirements and constraints
|
||||
- Explain architectural choices in terms of business and technical impact
|
||||
|
||||
**Deliverables:**
|
||||
- Working system architecture implementations
|
||||
- ADRs documenting architectural decisions
|
||||
- Service scaffolding and interface definitions
|
||||
- Database schemas and migration scripts
|
||||
- Configuration and deployment artifacts
|
||||
- Updated specifications with implementation status
|
||||
|
||||
**Anti-Patterns to Avoid:**
|
||||
- Premature optimization over correctness
|
||||
- Over-engineering for current needs
|
||||
- Building without clear requirements
|
||||
- Creating multiple sources of truth
|
||||
- Implementing solutions without understanding root causes
|
||||
|
||||
**Key Principles:**
|
||||
- Implement architectural decisions through working code
|
||||
- Document all significant decisions and trade-offs
|
||||
- Build systems that teams can understand and maintain
|
||||
- Apply proven patterns and avoid reinventing solutions
|
||||
- Balance current needs with long-term maintainability
|
||||
|
||||
When handed an architectural specification via `/spec implement`, you will read the spec, design the solution applying architectural principles, implement the necessary code and configuration, document decisions through ADRs, and update the spec with completion status and architectural notes.
|
||||
@@ -1,190 +0,0 @@
|
||||
# /project:check-health - Project Health Assessment
|
||||
|
||||
Comprehensive health check of the Basic Memory project including code quality, test coverage, dependencies, and documentation.
|
||||
|
||||
## Usage
|
||||
```
|
||||
/project:check-health
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
You are an expert DevOps engineer for the Basic Memory project. When the user runs `/project:check-health`, execute the following comprehensive assessment:
|
||||
|
||||
### Step 1: Git Repository Health
|
||||
1. **Repository Status**
|
||||
```bash
|
||||
git status
|
||||
git log --oneline -5
|
||||
git branch -vv
|
||||
```
|
||||
- Check working directory status
|
||||
- Verify branch alignment with remote
|
||||
- Check recent commit activity
|
||||
|
||||
2. **Branch Analysis**
|
||||
- Verify on main branch
|
||||
- Check if ahead/behind remote
|
||||
- Identify any untracked files
|
||||
|
||||
### Step 2: Code Quality Assessment
|
||||
1. **Linting and Formatting**
|
||||
```bash
|
||||
uv run ruff check .
|
||||
uv run pyright
|
||||
```
|
||||
- Count linting issues by severity
|
||||
- Check type annotation coverage
|
||||
- Verify code formatting compliance
|
||||
|
||||
2. **Test Suite Health**
|
||||
```bash
|
||||
uv run pytest --collect-only -q
|
||||
uv run pytest --co -q | wc -l
|
||||
```
|
||||
- Count total tests
|
||||
- Check for test discovery issues
|
||||
- Verify test structure integrity
|
||||
|
||||
### Step 3: Dependency Analysis
|
||||
1. **Dependency Health**
|
||||
```bash
|
||||
uv tree
|
||||
uv lock --dry-run
|
||||
```
|
||||
- Check for dependency conflicts
|
||||
- Identify outdated dependencies
|
||||
- Verify lock file consistency
|
||||
|
||||
2. **Security Scan**
|
||||
```bash
|
||||
uv run pip-audit --desc
|
||||
```
|
||||
- Scan for known vulnerabilities
|
||||
- Check dependency licenses
|
||||
- Identify security advisories
|
||||
|
||||
### Step 4: Performance Metrics
|
||||
1. **Test Performance**
|
||||
```bash
|
||||
uv run pytest --durations=10
|
||||
```
|
||||
- Identify slowest tests
|
||||
- Check overall test execution time
|
||||
- Monitor test suite growth
|
||||
|
||||
2. **Build Performance**
|
||||
```bash
|
||||
time uv run python -c "import basic_memory"
|
||||
```
|
||||
- Check import time
|
||||
- Validate package installation
|
||||
- Monitor startup performance
|
||||
|
||||
### Step 5: Documentation Health
|
||||
1. **Documentation Coverage**
|
||||
- Check README.md currency
|
||||
- Verify CLI documentation
|
||||
- Validate MCP tool documentation
|
||||
- Check changelog completeness
|
||||
|
||||
2. **API Documentation**
|
||||
- Verify docstring coverage
|
||||
- Check type annotation completeness
|
||||
- Validate example code
|
||||
|
||||
### Step 6: Project Metrics
|
||||
1. **Code Statistics**
|
||||
```bash
|
||||
find src -name "*.py" | xargs wc -l
|
||||
find tests -name "*.py" | xargs wc -l
|
||||
```
|
||||
- Lines of code trends
|
||||
- Test-to-code ratio
|
||||
- File organization metrics
|
||||
|
||||
## Health Report Format
|
||||
|
||||
Generate comprehensive health dashboard:
|
||||
|
||||
```
|
||||
🏥 Basic Memory Project Health Report
|
||||
|
||||
📊 OVERALL HEALTH: 🟢 EXCELLENT (92/100)
|
||||
|
||||
🗂️ GIT REPOSITORY
|
||||
✅ Clean working directory
|
||||
✅ Up to date with origin/main
|
||||
✅ Recent commit activity (5 commits this week)
|
||||
|
||||
🔍 CODE QUALITY
|
||||
✅ Linting: 0 errors, 2 warnings
|
||||
✅ Type checking: 100% coverage
|
||||
✅ Formatting: Compliant
|
||||
⚠️ Complex functions: 3 need refactoring
|
||||
|
||||
🧪 TEST SUITE
|
||||
✅ Total tests: 744
|
||||
✅ Test discovery: All tests found
|
||||
✅ Coverage: 98.2%
|
||||
⚡ Performance: 45.2s (good)
|
||||
|
||||
📦 DEPENDENCIES
|
||||
✅ Dependencies: Up to date
|
||||
✅ Security: No vulnerabilities
|
||||
✅ Conflicts: None detected
|
||||
⚠️ Outdated: 2 minor updates available
|
||||
|
||||
📖 DOCUMENTATION
|
||||
✅ README: Current
|
||||
✅ API docs: 95% coverage
|
||||
⚠️ CLI reference: Needs update
|
||||
✅ Changelog: Complete
|
||||
|
||||
📈 METRICS
|
||||
├── Source code: 15,432 lines
|
||||
├── Test code: 8,967 lines
|
||||
├── Test ratio: 58% (excellent)
|
||||
└── Complexity: Low (maintainable)
|
||||
|
||||
🎯 RECOMMENDATIONS:
|
||||
1. Update CLI documentation
|
||||
2. Refactor 3 complex functions
|
||||
3. Update minor dependencies
|
||||
4. Consider splitting large test files
|
||||
|
||||
🏆 PROJECT STATUS: Ready for v0.13.0 release!
|
||||
```
|
||||
|
||||
## Health Scoring
|
||||
|
||||
### Excellent (90-100)
|
||||
- All quality gates pass
|
||||
- High test coverage (>95%)
|
||||
- No security issues
|
||||
- Documentation current
|
||||
|
||||
### Good (75-89)
|
||||
- Minor issues present
|
||||
- Good test coverage (>90%)
|
||||
- No critical security issues
|
||||
- Most documentation current
|
||||
|
||||
### Needs Attention (60-74)
|
||||
- Several quality issues
|
||||
- Adequate test coverage (>80%)
|
||||
- Minor security concerns
|
||||
- Documentation gaps
|
||||
|
||||
### Critical (<60)
|
||||
- Major quality problems
|
||||
- Low test coverage (<80%)
|
||||
- Security vulnerabilities
|
||||
- Significant documentation issues
|
||||
|
||||
## Context
|
||||
- Provides comprehensive project overview
|
||||
- Identifies potential issues before they become problems
|
||||
- Tracks project health trends over time
|
||||
- Helps prioritize maintenance tasks
|
||||
- Supports release readiness decisions
|
||||
@@ -1,62 +0,0 @@
|
||||
# Basic Memory Custom Commands
|
||||
|
||||
This directory contains custom Claude Code slash commands for the Basic Memory project.
|
||||
|
||||
## Available Commands
|
||||
|
||||
### Release Management (`/project:release:*`)
|
||||
- `/project:release:beta` - Create beta releases with automated quality checks
|
||||
- `/project:release:release` - Create stable releases with comprehensive validation
|
||||
- `/project:release:release-check` - Pre-flight validation without making changes
|
||||
- `/project:release:changelog` - Generate changelog entries from commits
|
||||
|
||||
### Development (`/project:*`)
|
||||
- `/project:test-coverage` - Run tests with detailed coverage analysis
|
||||
- `/project:test-live` - Live testing suite using real Basic Memory installation
|
||||
- `/project:lint-fix` - Run comprehensive linting with auto-fix
|
||||
- `/project:check-health` - Comprehensive project health assessment
|
||||
|
||||
## Command Structure
|
||||
|
||||
Commands are organized by functionality:
|
||||
```
|
||||
.claude/commands/
|
||||
├── release/ # Release management commands
|
||||
│ ├── beta.md # /project:release:beta
|
||||
│ ├── release.md # /project:release:release
|
||||
│ ├── release-check.md # /project:release:release-check
|
||||
│ └── changelog.md # /project:release:changelog
|
||||
├── test-coverage.md # /project:test-coverage
|
||||
├── test-live.md # /project:test-live
|
||||
├── lint-fix.md # /project:lint-fix
|
||||
├── check-health.md # /project:check-health
|
||||
└── commands.md # This overview file
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Commands are invoked using the `/project:` prefix:
|
||||
- `/project:release:beta v0.13.0b4`
|
||||
- `/project:test-coverage mcp`
|
||||
- `/project:test-live core`
|
||||
- `/project:release:release-check`
|
||||
- `/project:check-health`
|
||||
|
||||
## Implementation
|
||||
|
||||
Each command is implemented as a Markdown file containing structured prompts that:
|
||||
1. Validate preconditions
|
||||
2. Execute steps in the correct order
|
||||
3. Handle errors gracefully
|
||||
4. Provide clear status updates
|
||||
5. Return actionable results
|
||||
|
||||
## Tooling Integration
|
||||
|
||||
Commands leverage existing project tooling:
|
||||
- `just check` - Quality checks
|
||||
- `just test` - Test suite
|
||||
- `just update-deps` - Dependency updates
|
||||
- `uv` - Package management
|
||||
- `git` - Version control
|
||||
- GitHub Actions - CI/CD pipeline
|
||||
@@ -1,145 +0,0 @@
|
||||
# /project:lint-fix - Comprehensive Code Quality Fix
|
||||
|
||||
Run comprehensive linting and auto-fix code quality issues across the codebase.
|
||||
|
||||
## Usage
|
||||
```
|
||||
/project:lint-fix
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
You are an expert code quality engineer for the Basic Memory project. When the user runs `/project:lint-fix`, execute the following steps:
|
||||
|
||||
### Step 1: Pre-flight Check
|
||||
1. **Verify Clean Working Directory**
|
||||
```bash
|
||||
git status --porcelain
|
||||
```
|
||||
- Check for uncommitted changes
|
||||
- Warn if working directory is not clean
|
||||
- Suggest stashing changes if needed
|
||||
|
||||
### Step 2: Import Organization
|
||||
1. **Fix Import Order and Cleanup**
|
||||
```bash
|
||||
uv run ruff check --select I --fix .
|
||||
```
|
||||
- Sort imports by category (standard, third-party, local)
|
||||
- Remove unused imports
|
||||
- Fix import spacing and organization
|
||||
|
||||
### Step 3: Code Formatting
|
||||
1. **Apply Consistent Formatting**
|
||||
```bash
|
||||
uv run ruff format .
|
||||
```
|
||||
- Format code according to project style
|
||||
- Fix line length issues (100 chars max)
|
||||
- Standardize quotes and spacing
|
||||
|
||||
### Step 4: Linting with Auto-fix
|
||||
1. **Fix Linting Issues**
|
||||
```bash
|
||||
uv run ruff check --fix .
|
||||
```
|
||||
- Auto-fix safe linting issues
|
||||
- Report any remaining manual fixes needed
|
||||
- Focus on code quality and best practices
|
||||
|
||||
### Step 5: Type Checking
|
||||
1. **Validate Type Annotations**
|
||||
```bash
|
||||
uv run pyright
|
||||
```
|
||||
- Check for type errors
|
||||
- Report any missing type annotations
|
||||
- Validate type compatibility
|
||||
|
||||
### Step 6: Report Generation
|
||||
Generate comprehensive quality report:
|
||||
|
||||
```
|
||||
🔧 Code Quality Fix Report
|
||||
|
||||
✅ FIXES APPLIED:
|
||||
├── Import organization: 12 files updated
|
||||
├── Code formatting: 8 files reformatted
|
||||
├── Auto-fixable lint issues: 23 issues resolved
|
||||
└── Total files processed: 156
|
||||
|
||||
⚠️ MANUAL ATTENTION NEEDED:
|
||||
├── Type annotations missing in entity_service.py:45
|
||||
├── Complex function needs refactoring in sync_service.py:123
|
||||
└── Unused variable in test_utils.py:67
|
||||
|
||||
🎯 QUALITY SCORE: 96.2% (excellent)
|
||||
|
||||
📁 Run `git diff` to review all changes
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Issues
|
||||
- **Merge Conflicts**: Provide resolution guidance
|
||||
- **Syntax Errors**: Point to specific files and lines
|
||||
- **Type Errors**: Suggest specific fixes
|
||||
- **Import Errors**: Check for missing dependencies
|
||||
|
||||
### Recovery Steps
|
||||
- If auto-fixes introduce issues, provide rollback instructions
|
||||
- If type checking fails, suggest incremental fixes
|
||||
- If tests break, provide debugging guidance
|
||||
|
||||
## Quality Gates
|
||||
|
||||
### Must Pass
|
||||
- [ ] All auto-fixable lint issues resolved
|
||||
- [ ] Code formatting consistent
|
||||
- [ ] No syntax errors
|
||||
- [ ] Import organization clean
|
||||
|
||||
### Should Pass (Warnings)
|
||||
- [ ] No type checking errors
|
||||
- [ ] No complex function warnings
|
||||
- [ ] No unused variables/imports
|
||||
- [ ] Consistent naming conventions
|
||||
|
||||
## Output Examples
|
||||
|
||||
### Successful Fix
|
||||
```
|
||||
🎉 CODE QUALITY IMPROVED!
|
||||
|
||||
✅ All auto-fixes applied successfully
|
||||
📏 Code formatting: 100% compliant
|
||||
🔍 Linting: No issues found
|
||||
🏷️ Type checking: All passed
|
||||
|
||||
Ready for commit! Use:
|
||||
git add -A && git commit -m "style: fix code quality issues"
|
||||
```
|
||||
|
||||
### Issues Requiring Attention
|
||||
```
|
||||
⚠️ PARTIAL SUCCESS - MANUAL FIXES NEEDED
|
||||
|
||||
✅ Auto-fixes applied: 45 issues
|
||||
❌ Manual fixes needed: 3 issues
|
||||
|
||||
Priority fixes:
|
||||
1. Fix type annotation in services/entity_service.py:142
|
||||
2. Simplify complex function in sync/sync_service.py:67
|
||||
3. Remove unused import in tests/conftest.py:12
|
||||
|
||||
Run these commands:
|
||||
# Fix specific file
|
||||
uv run pyright src/basic_memory/services/entity_service.py
|
||||
```
|
||||
|
||||
## Context
|
||||
- Uses ruff for fast Python linting and formatting
|
||||
- Uses pyright for type checking
|
||||
- Follows project code style guidelines (100 char line length)
|
||||
- Maintains backward compatibility
|
||||
- Integrates with existing pre-commit hooks
|
||||
@@ -1,69 +1,95 @@
|
||||
# /beta - Create Beta Release
|
||||
|
||||
Create a new beta release for the current version with automated quality checks and tagging.
|
||||
Create a new beta release using the automated justfile target with quality checks and tagging.
|
||||
|
||||
## Usage
|
||||
```
|
||||
/beta [version]
|
||||
/beta <version>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `version` (optional): Beta version like `v0.13.0b4`. If not provided, auto-increments from latest beta tag.
|
||||
- `version` (required): Beta version like `v0.13.2b1` or `v0.13.2rc1`
|
||||
|
||||
## Implementation
|
||||
|
||||
You are an expert release manager for the Basic Memory project. When the user runs `/beta`, execute the following steps:
|
||||
|
||||
### Step 1: Pre-flight Checks
|
||||
1. Check current git status for uncommitted changes
|
||||
2. Verify we're on the `main` branch
|
||||
3. Get the latest beta tag to determine next version if not provided
|
||||
### Step 1: Pre-flight Validation
|
||||
1. Verify version format matches `v\d+\.\d+\.\d+(b\d+|rc\d+)` pattern
|
||||
2. Check current git status for uncommitted changes
|
||||
3. Verify we're on the `main` branch
|
||||
4. Confirm no existing tag with this version
|
||||
|
||||
### Step 2: Quality Assurance
|
||||
1. Run `just check` to ensure code quality
|
||||
2. If any checks fail, report issues and stop
|
||||
3. Run `just update-deps` to ensure latest dependencies
|
||||
4. Commit any dependency updates with proper message
|
||||
### Step 2: Use Justfile Automation
|
||||
Execute the automated beta release process:
|
||||
```bash
|
||||
just beta <version>
|
||||
```
|
||||
|
||||
### Step 3: Version Determination
|
||||
If version not provided:
|
||||
1. Get latest git tags with `git tag -l "v*b*" --sort=-version:refname | head -1`
|
||||
2. Auto-increment beta number (e.g., `v0.13.0b2` → `v0.13.0b3`)
|
||||
3. Confirm version with user before proceeding
|
||||
The justfile target handles:
|
||||
- ✅ Beta version format validation (supports b1, b2, rc1, etc.)
|
||||
- ✅ Git status and branch checks
|
||||
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
|
||||
- ✅ Version update in `src/basic_memory/__init__.py`
|
||||
- ✅ Automatic commit with proper message
|
||||
- ✅ Tag creation and pushing to GitHub
|
||||
- ✅ Beta release workflow trigger
|
||||
|
||||
### Step 4: Release Creation
|
||||
1. Commit any remaining changes
|
||||
2. Push to main: `git push origin main`
|
||||
3. Create tag: `git tag {version}`
|
||||
4. Push tag: `git push origin {version}`
|
||||
|
||||
### Step 5: Monitor Release
|
||||
### Step 3: Monitor Beta Release
|
||||
1. Check GitHub Actions workflow starts successfully
|
||||
2. Provide installation instructions for beta
|
||||
3. Report status and next steps
|
||||
2. Monitor workflow at: https://github.com/basicmachines-co/basic-memory/actions
|
||||
3. Verify PyPI pre-release publication
|
||||
4. Test beta installation: `uv tool install basic-memory --pre`
|
||||
|
||||
### Step 4: Beta Testing Instructions
|
||||
Provide users with beta testing instructions:
|
||||
|
||||
```bash
|
||||
# Install/upgrade to beta
|
||||
uv tool install basic-memory --pre
|
||||
|
||||
# Or upgrade existing installation
|
||||
uv tool upgrade basic-memory --prerelease=allow
|
||||
```
|
||||
|
||||
## Version Guidelines
|
||||
- **First beta**: `v0.13.2b1`
|
||||
- **Subsequent betas**: `v0.13.2b2`, `v0.13.2b3`, etc.
|
||||
- **Release candidates**: `v0.13.2rc1`, `v0.13.2rc2`, etc.
|
||||
- **Final release**: `v0.13.2` (use `/release` command)
|
||||
|
||||
## Error Handling
|
||||
- If quality checks fail, provide specific fix instructions
|
||||
- If git operations fail, provide manual recovery steps
|
||||
- If GitHub Actions fail, provide debugging guidance
|
||||
- If `just beta` fails, examine the error output for specific issues
|
||||
- If quality checks fail, fix issues and retry
|
||||
- If version format is invalid, correct and retry
|
||||
- If tag already exists, increment version number
|
||||
|
||||
## Success Output
|
||||
```
|
||||
✅ Beta Release v0.13.0b4 Created Successfully!
|
||||
✅ Beta Release v0.13.2b1 Created Successfully!
|
||||
|
||||
🏷️ Tag: v0.13.0b4
|
||||
🏷️ Tag: v0.13.2b1
|
||||
🚀 GitHub Actions: Running
|
||||
📦 PyPI: Will be available in ~5 minutes
|
||||
📦 PyPI: Will be available in ~5 minutes as pre-release
|
||||
|
||||
Install with:
|
||||
uv tool upgrade basic-memory --prerelease=allow
|
||||
Install/test with:
|
||||
uv tool install basic-memory --pre
|
||||
|
||||
Monitor release: https://github.com/basicmachines-co/basic-memory/actions
|
||||
```
|
||||
|
||||
## Beta Testing Workflow
|
||||
1. **Create beta**: Use `/beta v0.13.2b1`
|
||||
2. **Test features**: Install and validate new functionality
|
||||
3. **Fix issues**: Address bugs found during testing
|
||||
4. **Iterate**: Create `v0.13.2b2` if needed
|
||||
5. **Release candidate**: Create `v0.13.2rc1` when stable
|
||||
6. **Final release**: Use `/release v0.13.2` when ready
|
||||
|
||||
## Context
|
||||
- Use the existing justfile targets (`just check`, `just update-deps`)
|
||||
- Follow semantic versioning for beta releases
|
||||
- Maintain release notes in CHANGELOG.md
|
||||
- Use conventional commit messages
|
||||
- Leverage uv-dynamic-versioning for version management
|
||||
- Beta releases are pre-releases for testing new features
|
||||
- Automatically published to PyPI with pre-release flag
|
||||
- Uses the automated justfile target for consistency
|
||||
- Version is automatically updated in `__init__.py`
|
||||
- Ideal for validating changes before stable release
|
||||
- Supports both beta (b1, b2) and release candidate (rc1, rc2) versions
|
||||
@@ -8,7 +8,7 @@ Analyze commits and generate formatted changelog entry for a version.
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `version` (required): Version like `v0.13.0` or `v0.13.0b4`
|
||||
- `version` (required): Version like `v0.14.0` or `v0.14.0b1`
|
||||
- `type` (optional): `beta`, `rc`, or `stable` (default: `stable`)
|
||||
|
||||
## Implementation
|
||||
@@ -59,8 +59,9 @@ You are an expert technical writer for the Basic Memory project. When the user r
|
||||
### Step 3: Generate Changelog Entry
|
||||
Create formatted entry following existing CHANGELOG.md style:
|
||||
|
||||
Example:
|
||||
```markdown
|
||||
## v0.13.0 (2025-06-03)
|
||||
## <version> (<date>)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -128,6 +129,8 @@ Create formatted entry following existing CHANGELOG.md style:
|
||||
## Output Format
|
||||
|
||||
### For Beta Releases
|
||||
|
||||
Example:
|
||||
```markdown
|
||||
## v0.13.0b4 (2025-06-03)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# /release - Create Stable Release
|
||||
|
||||
Create a stable release from the current main branch with comprehensive validation.
|
||||
Create a stable release using the automated justfile target with comprehensive validation.
|
||||
|
||||
## Usage
|
||||
```
|
||||
@@ -8,77 +8,162 @@ Create a stable release from the current main branch with comprehensive validati
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `version` (required): Release version like `v0.13.0`
|
||||
- `version` (required): Release version like `v0.13.2`
|
||||
|
||||
## Implementation
|
||||
|
||||
You are an expert release manager for the Basic Memory project. When the user runs `/release`, execute the following steps:
|
||||
|
||||
### Step 1: Pre-flight Validation
|
||||
1. Verify version format matches `v\d+\.\d+\.\d+` pattern
|
||||
2. Check current git status for uncommitted changes
|
||||
3. Verify we're on the `main` branch
|
||||
4. Confirm no existing tag with this version
|
||||
|
||||
### Step 2: Comprehensive Quality Checks
|
||||
1. Run `just check` (lint, format, type-check, full test suite)
|
||||
2. Verify test coverage meets minimum requirements (95%+)
|
||||
3. Check that CHANGELOG.md contains entry for this version
|
||||
4. Validate all high-priority issues are closed
|
||||
#### Version Check
|
||||
1. Check current version in `src/basic_memory/__init__.py`
|
||||
2. Verify new version format matches `v\d+\.\d+\.\d+` pattern
|
||||
3. Confirm version is higher than current version
|
||||
|
||||
### Step 3: Release Preparation
|
||||
1. Update any version references if needed
|
||||
2. Commit any final changes with message: `chore: prepare for ${version} release`
|
||||
3. Push to main: `git push origin main`
|
||||
#### Git Status
|
||||
1. Check current git status for uncommitted changes
|
||||
2. Verify we're on the `main` branch
|
||||
3. Confirm no existing tag with this version
|
||||
|
||||
### Step 4: Release Creation
|
||||
1. Create annotated tag: `git tag -a ${version} -m "Release ${version}"`
|
||||
2. Push tag: `git push origin ${version}`
|
||||
3. Monitor GitHub Actions for release automation
|
||||
#### Documentation Validation
|
||||
1. **Changelog Check**
|
||||
- CHANGELOG.md contains entry for target version
|
||||
- Entry includes all major features and fixes
|
||||
- Breaking changes are documented
|
||||
|
||||
### Step 5: Post-Release Validation
|
||||
1. Verify GitHub release is created automatically
|
||||
2. Check PyPI publication
|
||||
3. Validate release assets
|
||||
4. Test installation: `uv tool install basic-memory`
|
||||
### Step 2: Use Justfile Automation
|
||||
Execute the automated release process:
|
||||
```bash
|
||||
just release <version>
|
||||
```
|
||||
|
||||
### Step 6: Documentation Update
|
||||
1. Update any post-release documentation
|
||||
2. Create follow-up tasks if needed
|
||||
The justfile target handles:
|
||||
- ✅ Version format validation
|
||||
- ✅ Git status and branch checks
|
||||
- ✅ Quality checks (`just check` - lint, format, type-check, tests)
|
||||
- ✅ Version update in `src/basic_memory/__init__.py`
|
||||
- ✅ Automatic commit with proper message
|
||||
- ✅ Tag creation and pushing to GitHub
|
||||
- ✅ Release workflow trigger (automatic on tag push)
|
||||
|
||||
The GitHub Actions workflow (`.github/workflows/release.yml`) then:
|
||||
- ✅ Builds the package using `uv build`
|
||||
- ✅ Creates GitHub release with auto-generated notes
|
||||
- ✅ Publishes to PyPI
|
||||
- ✅ Updates Homebrew formula (stable releases only)
|
||||
|
||||
### Step 3: Monitor Release Process
|
||||
1. Verify tag push triggered the workflow (should start automatically within seconds)
|
||||
2. Monitor workflow progress at: https://github.com/basicmachines-co/basic-memory/actions
|
||||
3. Watch for successful completion of both jobs:
|
||||
- `release` - Builds package and publishes to PyPI
|
||||
- `homebrew` - Updates Homebrew formula (stable releases only)
|
||||
4. Check for any workflow failures and investigate logs if needed
|
||||
|
||||
### Step 4: Post-Release Validation
|
||||
|
||||
#### GitHub Release
|
||||
1. Verify GitHub release is created at: https://github.com/basicmachines-co/basic-memory/releases/tag/<version>
|
||||
2. Check that release notes are auto-generated from commits
|
||||
3. Validate release assets (`.whl` and `.tar.gz` files are attached)
|
||||
|
||||
#### PyPI Publication
|
||||
1. Verify package published at: https://pypi.org/project/basic-memory/<version>/
|
||||
2. Test installation: `uv tool install basic-memory`
|
||||
3. Verify installed version: `basic-memory --version`
|
||||
|
||||
#### Homebrew Formula (Stable Releases Only)
|
||||
1. Check formula update at: https://github.com/basicmachines-co/homebrew-basic-memory
|
||||
2. Verify formula version matches release
|
||||
3. Test Homebrew installation: `brew install basicmachines-co/basic-memory/basic-memory`
|
||||
|
||||
#### Website Updates
|
||||
|
||||
**1. basicmachines.co** (`/Users/drew/code/basicmachines.co`)
|
||||
- **Goal**: Update version number displayed on the homepage
|
||||
- **Location**: Search for "Basic Memory v0." in the codebase to find version displays
|
||||
- **What to update**:
|
||||
- Hero section heading that shows "Basic Memory v{VERSION}"
|
||||
- "What's New in v{VERSION}" section heading
|
||||
- Feature highlights array (look for array of features with title/description)
|
||||
- **Process**:
|
||||
1. Pull latest from GitHub: `git pull origin main`
|
||||
2. Create release branch: `git checkout -b release/v{VERSION}`
|
||||
3. Search codebase for current version number (e.g., "v0.16.1")
|
||||
4. Update version numbers to new release version
|
||||
5. Update feature highlights with 3-5 key features from this release (extract from CHANGELOG.md)
|
||||
6. Commit changes: `git commit -m "chore: update to v{VERSION}"`
|
||||
7. Push branch: `git push origin release/v{VERSION}`
|
||||
- **Deploy**: Follow deployment process for basicmachines.co
|
||||
|
||||
**2. docs.basicmemory.com** (`/Users/drew/code/docs.basicmemory.com`)
|
||||
- **Goal**: Add new release notes section to the latest-releases page
|
||||
- **File**: `src/pages/latest-releases.mdx`
|
||||
- **What to do**:
|
||||
1. Pull latest from GitHub: `git pull origin main`
|
||||
2. Create release branch: `git checkout -b release/v{VERSION}`
|
||||
3. Read the existing file to understand the format and structure
|
||||
4. Read `/Users/drew/code/basic-memory/CHANGELOG.md` to get release content
|
||||
5. Add new release section **at the top** (after MDX imports, before other releases)
|
||||
6. Follow the existing pattern:
|
||||
- Heading: `## [v{VERSION}](github-link) — YYYY-MM-DD`
|
||||
- Focus statement if applicable
|
||||
- `<Info>` block with highlights (3-5 key items)
|
||||
- Sections for Features, Bug Fixes, Breaking Changes, etc.
|
||||
- Link to full changelog at the end
|
||||
- Separator `---` between releases
|
||||
7. Commit changes: `git commit -m "docs: add v{VERSION} release notes"`
|
||||
8. Push branch: `git push origin release/v{VERSION}`
|
||||
- **Source content**: Extract and format sections from CHANGELOG.md for this version
|
||||
- **Deploy**: Follow deployment process for docs.basicmemory.com
|
||||
|
||||
**4. Announce Release**
|
||||
- Post to Discord community if significant changes
|
||||
- Update social media if major release
|
||||
- Notify users via appropriate channels
|
||||
|
||||
## Pre-conditions Check
|
||||
Before starting, verify:
|
||||
- [ ] All beta testing is complete
|
||||
- [ ] Critical bugs are fixed
|
||||
- [ ] Breaking changes are documented
|
||||
- [ ] CHANGELOG.md is updated
|
||||
- [ ] CHANGELOG.md is updated (if needed)
|
||||
- [ ] Version number follows semantic versioning
|
||||
|
||||
## Error Handling
|
||||
- If any quality check fails, stop and provide fix instructions
|
||||
- If changelog entry missing, prompt to create one
|
||||
- If tests fail, provide debugging guidance
|
||||
- If GitHub Actions fail, provide manual release steps
|
||||
- If `just release` fails, examine the error output for specific issues
|
||||
- If quality checks fail, fix issues and retry
|
||||
- If changelog entry missing, update CHANGELOG.md and commit before retrying
|
||||
- If GitHub Actions fail, check workflow logs for debugging
|
||||
|
||||
## Success Output
|
||||
```
|
||||
🎉 Stable Release v0.13.0 Created Successfully!
|
||||
🎉 Stable Release v0.13.2 Created Successfully!
|
||||
|
||||
🏷️ Tag: v0.13.0
|
||||
📋 GitHub Release: https://github.com/basicmachines-co/basic-memory/releases/tag/v0.13.0
|
||||
📦 PyPI: https://pypi.org/project/basic-memory/0.13.0/
|
||||
🏷️ Tag: v0.13.2
|
||||
📋 GitHub Release: https://github.com/basicmachines-co/basic-memory/releases/tag/v0.13.2
|
||||
📦 PyPI: https://pypi.org/project/basic-memory/0.13.2/
|
||||
🍺 Homebrew: https://github.com/basicmachines-co/homebrew-basic-memory
|
||||
🚀 GitHub Actions: Completed
|
||||
|
||||
Install with:
|
||||
uv tool install basic-memory
|
||||
Install with pip/uv:
|
||||
uv tool install basic-memory
|
||||
|
||||
Install with Homebrew:
|
||||
brew install basicmachines-co/basic-memory/basic-memory
|
||||
|
||||
Users can now upgrade:
|
||||
uv tool upgrade basic-memory
|
||||
uv tool upgrade basic-memory
|
||||
brew upgrade basic-memory
|
||||
```
|
||||
|
||||
## Context
|
||||
- This creates production releases used by end users
|
||||
- Must pass all quality gates before proceeding
|
||||
- Follows the release workflow documented in CLAUDE.md
|
||||
- Uses uv-dynamic-versioning for automatic version management
|
||||
- Triggers automated GitHub release with changelog
|
||||
- Uses the automated justfile target for consistency
|
||||
- Version is automatically updated in `__init__.py`
|
||||
- Triggers automated GitHub release with changelog
|
||||
- Package is published to PyPI for `pip` and `uv` users
|
||||
- Homebrew formula is automatically updated for stable releases
|
||||
- Supports multiple installation methods (uv, pip, Homebrew)
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
allowed-tools: mcp__basic-memory__write_note, mcp__basic-memory__read_note, mcp__basic-memory__search_notes, mcp__basic-memory__edit_note
|
||||
argument-hint: [create|status|show|review] [spec-name]
|
||||
description: Manage specifications in our development process
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Specifications are managed in the Basic Memory "specs" project. All specs live in a centralized location accessible across all repositories via MCP tools.
|
||||
|
||||
See SPEC-1 and SPEC-2 in the "specs" project for the full specification-driven development process.
|
||||
|
||||
Available commands:
|
||||
- `create [name]` - Create new specification
|
||||
- `status` - Show all spec statuses
|
||||
- `show [spec-name]` - Read a specific spec
|
||||
- `review [spec-name]` - Review implementation against spec
|
||||
|
||||
## Your task
|
||||
|
||||
Execute the spec command: `/spec $ARGUMENTS`
|
||||
|
||||
### If command is "create":
|
||||
1. Get next SPEC number by searching existing specs in "specs" project
|
||||
2. Create new spec using template from SPEC-2
|
||||
3. Use mcp__basic-memory__write_note with project="specs"
|
||||
4. Include standard sections: Why, What, How, How to Evaluate
|
||||
|
||||
### If command is "status":
|
||||
1. Use mcp__basic-memory__search_notes with project="specs"
|
||||
2. Display table with spec number, title, and progress
|
||||
3. Show completion status from checkboxes in content
|
||||
|
||||
### If command is "show":
|
||||
1. Use mcp__basic-memory__read_note with project="specs"
|
||||
2. Display the full spec content
|
||||
|
||||
### If command is "review":
|
||||
1. Read the specified spec and its "How to Evaluate" section
|
||||
2. Review current implementation against success criteria with careful evaluation of:
|
||||
- **Functional completeness** - All specified features working
|
||||
- **Test coverage analysis** - Actual test files and coverage percentage
|
||||
- Count existing test files vs required components/APIs/composables
|
||||
- Verify unit tests, integration tests, and end-to-end tests
|
||||
- Check for missing test categories (component, API, workflow)
|
||||
- **Code quality metrics** - TypeScript compilation, linting, performance
|
||||
- **Architecture compliance** - Component isolation, state management patterns
|
||||
- **Documentation completeness** - Implementation matches specification
|
||||
3. Provide honest, accurate assessment - do not overstate completeness
|
||||
4. Document findings and update spec with review results using mcp__basic-memory__edit_note
|
||||
5. If gaps found, clearly identify what still needs to be implemented/tested
|
||||
@@ -1,131 +0,0 @@
|
||||
# /test-coverage - Run Tests with Coverage Analysis
|
||||
|
||||
Execute test suite with comprehensive coverage reporting and analysis.
|
||||
|
||||
## Usage
|
||||
```
|
||||
/test-coverage [pattern]
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `pattern` (optional): Test pattern to run specific tests (e.g., `test_mcp`, `*integration*`)
|
||||
|
||||
## Implementation
|
||||
|
||||
You are an expert QA engineer for the Basic Memory project. When the user runs `/test-coverage`, execute the following steps:
|
||||
|
||||
### Step 1: Test Execution
|
||||
1. **Run Tests with Coverage**
|
||||
```bash
|
||||
# Full test suite
|
||||
uv run pytest --cov=basic_memory --cov-report=html --cov-report=term -v
|
||||
|
||||
# Or with pattern if provided
|
||||
uv run pytest tests/*{pattern}* --cov=basic_memory --cov-report=html --cov-report=term -v
|
||||
```
|
||||
|
||||
2. **Generate Coverage Reports**
|
||||
- Terminal summary with percentages
|
||||
- HTML report for detailed analysis
|
||||
- Identify files below coverage threshold
|
||||
|
||||
### Step 2: Coverage Analysis
|
||||
1. **Summary Statistics**
|
||||
- Overall coverage percentage
|
||||
- Number of files with 100% coverage
|
||||
- Files below 95% threshold
|
||||
- Total lines covered/missed
|
||||
|
||||
2. **Detailed Breakdown**
|
||||
- Coverage by module/package
|
||||
- Identify untested code paths
|
||||
- Find missing edge case tests
|
||||
|
||||
### Step 3: Report Generation
|
||||
Generate comprehensive coverage report:
|
||||
|
||||
```
|
||||
🧪 Test Coverage Report
|
||||
|
||||
📊 OVERALL COVERAGE: 98.2% (target: 95%+)
|
||||
|
||||
✅ EXCELLENT COVERAGE (>95%):
|
||||
├── basic_memory/mcp/: 99.1%
|
||||
├── basic_memory/services/: 98.8%
|
||||
├── basic_memory/repository/: 97.9%
|
||||
└── basic_memory/api/: 96.2%
|
||||
|
||||
⚠️ NEEDS ATTENTION (<95%):
|
||||
├── basic_memory/sync/: 94.1% (missing 12 lines)
|
||||
└── basic_memory/importers/: 91.8% (missing 23 lines)
|
||||
|
||||
🎯 SPECIFIC GAPS:
|
||||
├── sync_service.py:142-145 (error handling)
|
||||
├── importer_base.py:67-70 (edge case)
|
||||
└── file_utils.py:89 (exception path)
|
||||
|
||||
📁 HTML Report: htmlcov/index.html
|
||||
🚀 Run `open htmlcov/index.html` to view detailed report
|
||||
```
|
||||
|
||||
### Step 4: Actionable Recommendations
|
||||
1. **Coverage Improvements**
|
||||
- Suggest specific tests to add
|
||||
- Identify edge cases to cover
|
||||
- Recommend integration tests
|
||||
|
||||
2. **Quality Insights**
|
||||
- Highlight well-tested modules
|
||||
- Point out testing patterns to follow
|
||||
- Suggest refactoring for testability
|
||||
|
||||
## Advanced Analysis
|
||||
|
||||
### Performance Metrics
|
||||
- Test execution time by module
|
||||
- Slowest tests identification
|
||||
- Coverage collection overhead
|
||||
|
||||
### Integration Coverage
|
||||
- MCP tool integration tests
|
||||
- API endpoint coverage
|
||||
- Database operation coverage
|
||||
- File system operation coverage
|
||||
|
||||
## Output Examples
|
||||
|
||||
### Full Coverage Success
|
||||
```
|
||||
🎉 EXCELLENT COVERAGE!
|
||||
|
||||
📊 Coverage: 98.7% (744 tests passed)
|
||||
✅ All modules above 95% threshold
|
||||
🏆 23 files with 100% coverage
|
||||
⚡ Tests completed in 45.2s
|
||||
|
||||
Ready for release! 🚀
|
||||
```
|
||||
|
||||
### Coverage Issues Found
|
||||
```
|
||||
⚠️ COVERAGE GAPS DETECTED
|
||||
|
||||
📊 Coverage: 92.1% (below 95% target)
|
||||
❌ 3 modules need attention
|
||||
🔍 43 uncovered lines found
|
||||
|
||||
Priority fixes:
|
||||
1. Add tests for error handling in sync_service.py
|
||||
2. Cover edge cases in importer_base.py
|
||||
3. Test exception paths in file_utils.py
|
||||
|
||||
Run specific tests:
|
||||
uv run pytest tests/sync/ -v
|
||||
```
|
||||
|
||||
## Context
|
||||
- Uses pytest with coverage plugin
|
||||
- Generates both terminal and HTML reports
|
||||
- Focuses on actionable improvement suggestions
|
||||
- Integrates with existing test infrastructure
|
||||
- Helps maintain high code quality standards
|
||||
@@ -1,6 +1,7 @@
|
||||
# /project:test-live - Live Basic Memory Testing Suite
|
||||
|
||||
Execute comprehensive real-world testing of Basic Memory using the installed version, following the methodology in TESTING.md. All test results are recorded as notes in a dedicated test project.
|
||||
Execute comprehensive real-world testing of Basic Memory using the installed version.
|
||||
All test results are recorded as notes in a dedicated test project.
|
||||
|
||||
## Usage
|
||||
```
|
||||
@@ -8,12 +9,81 @@ Execute comprehensive real-world testing of Basic Memory using the installed ver
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `phase` (optional): Specific test phase to run (`core`, `features`, `edge`, `workflows`, `stress`, or `all`)
|
||||
- `phase` (optional): Specific test phase to run (`recent`, `core`, `features`, `edge`, `workflows`, `stress`, or `all`)
|
||||
- `recent` - Focus on recent changes and new features (recommended for regular testing)
|
||||
- `core` - Essential tools only (Tier 1: write_note, read_note, search_notes, edit_note, list_memory_projects, recent_activity)
|
||||
- `features` - Core + important workflows (Tier 1 + Tier 2)
|
||||
- `all` - Comprehensive testing of all tools and scenarios
|
||||
|
||||
## Implementation
|
||||
|
||||
You are an expert QA engineer conducting live testing of Basic Memory.
|
||||
When the user runs `/project:test-live`, execute comprehensive testing following the TESTING.md methodology:
|
||||
When the user runs `/project:test-live`, execute comprehensive test plan:
|
||||
|
||||
## Tool Testing Priority
|
||||
|
||||
### **Tier 1: Critical Core (Always Test)**
|
||||
1. **write_note** - Foundation of all knowledge creation
|
||||
2. **read_note** - Primary knowledge retrieval mechanism
|
||||
3. **search_notes** - Essential for finding information
|
||||
4. **edit_note** - Core content modification capability
|
||||
5. **list_memory_projects** - Project discovery and session guidance
|
||||
6. **recent_activity** - Project discovery mode and activity analysis
|
||||
|
||||
### **Tier 2: Important Workflows (Usually Test)**
|
||||
7. **build_context** - Conversation continuity via memory:// URLs
|
||||
8. **create_memory_project** - Essential for project setup
|
||||
9. **move_note** - Knowledge organization
|
||||
10. **sync_status** - Understanding system state
|
||||
11. **delete_project** - Project lifecycle management
|
||||
|
||||
### **Tier 3: Enhanced Functionality (Sometimes Test)**
|
||||
12. **view_note** - Claude Desktop artifact display
|
||||
13. **read_content** - Raw content access
|
||||
14. **delete_note** - Content removal
|
||||
15. **list_directory** - File system exploration
|
||||
16. **edit_note** (advanced modes) - Complex find/replace operations
|
||||
|
||||
### **Tier 4: Specialized (Rarely Test)**
|
||||
17. **canvas** - Obsidian visualization (specialized use case)
|
||||
18. **MCP Prompts** - Enhanced UX tools (ai_assistant_guide, continue_conversation)
|
||||
|
||||
## Stateless Architecture Testing
|
||||
|
||||
### **Project Discovery Workflow (CRITICAL)**
|
||||
Test the new stateless project selection flow:
|
||||
|
||||
1. **Initial Discovery**
|
||||
- Call `list_memory_projects()` without knowing which project to use
|
||||
- Verify clear session guidance appears: "Next: Ask which project to use"
|
||||
- Confirm removal of CLI-specific references
|
||||
|
||||
2. **Activity-Based Discovery**
|
||||
- Call `recent_activity()` without project parameter (discovery mode)
|
||||
- Verify intelligent project suggestions based on activity
|
||||
- Test guidance: "Should I use [most-active-project] for this task?"
|
||||
|
||||
3. **Session Tracking Validation**
|
||||
- Verify all tool responses include `[Session: Using project 'name']`
|
||||
- Confirm guidance reminds about session-wide project tracking
|
||||
|
||||
4. **Single Project Constraint Mode**
|
||||
- Test MCP server with `--project` parameter
|
||||
- Verify all operations constrained to specified project
|
||||
- Test project override behavior in constrained mode
|
||||
|
||||
### **Explicit Project Parameters (CRITICAL)**
|
||||
All tools must require explicit project parameters:
|
||||
|
||||
1. **Parameter Validation**
|
||||
- Test all Tier 1 tools require `project` parameter
|
||||
- Verify clear error messages for missing project
|
||||
- Test invalid project name handling
|
||||
|
||||
2. **No Session State Dependencies**
|
||||
- Confirm no tool relies on "current project" concept
|
||||
- Test rapid project switching within conversation
|
||||
- Verify each call is truly independent
|
||||
|
||||
### Pre-Test Setup
|
||||
|
||||
@@ -22,7 +92,13 @@ When the user runs `/project:test-live`, execute comprehensive testing following
|
||||
- Check version and confirm it's the expected release
|
||||
- Test MCP connection and tool availability
|
||||
|
||||
2. **Test Project Creation**
|
||||
2. **Recent Changes Analysis** (if phase includes 'recent' or 'all')
|
||||
- Run `git log --oneline -20` to examine recent commits
|
||||
- Identify new features, bug fixes, and enhancements
|
||||
- Generate targeted test scenarios for recent changes
|
||||
- Prioritize regression testing for recently fixed issues
|
||||
|
||||
3. **Test Project Creation**
|
||||
|
||||
Run the bash `date` command to get the current date/time.
|
||||
|
||||
@@ -32,85 +108,151 @@ Run the bash `date` command to get the current date/time.
|
||||
Purpose: Record all test observations and results
|
||||
```
|
||||
|
||||
Make sure to switch to the newly created project with the `switch_project()` tool.
|
||||
Make sure to use the newly created project for all subsequent test operations by specifying it in the `project` parameter of each tool call.
|
||||
|
||||
3. **Baseline Documentation**
|
||||
4. **Baseline Documentation**
|
||||
Create initial test session note with:
|
||||
- Test environment details
|
||||
- Version being tested
|
||||
- Recent changes identified (if applicable)
|
||||
- Test objectives and scope
|
||||
- Start timestamp
|
||||
|
||||
### Phase 1: Core Functionality Validation
|
||||
### Phase 0: Recent Changes Validation (if 'recent' or 'all' phase)
|
||||
|
||||
Test all fundamental MCP tools systematically:
|
||||
Based on recent commit analysis, create targeted test scenarios:
|
||||
|
||||
**write_note Tests:**
|
||||
- Basic note creation with various content types
|
||||
- Frontmatter handling (tags, custom fields)
|
||||
- Special characters in titles and content
|
||||
- Unicode and emoji support
|
||||
- Empty notes and minimal content
|
||||
**Recent Changes Test Protocol:**
|
||||
1. **Feature Addition Tests** - For each new feature identified:
|
||||
- Test basic functionality
|
||||
- Test integration with existing tools
|
||||
- Verify documentation accuracy
|
||||
- Test edge cases and error handling
|
||||
|
||||
**read_note Tests:**
|
||||
- Read by title, permalink, memory:// URLs
|
||||
- Non-existent notes (error handling)
|
||||
- Notes with complex formatting
|
||||
- Performance with large notes
|
||||
2. **Bug Fix Regression Tests** - For each recent fix:
|
||||
- Recreate the original problem scenario
|
||||
- Verify the fix works as expected
|
||||
- Test related functionality isn't broken
|
||||
- Document the verification in test notes
|
||||
|
||||
**view_note Tests:**
|
||||
- View notes as formatted artifacts (Claude Desktop)
|
||||
- Title extraction from frontmatter and headings
|
||||
- Unicode and emoji content in artifacts
|
||||
- Error handling for non-existent notes
|
||||
- Artifact display quality and readability
|
||||
3. **Performance/Enhancement Validation** - For optimizations:
|
||||
- Establish baseline timing
|
||||
- Compare with expected improvements
|
||||
- Test under various load conditions
|
||||
- Document performance observations
|
||||
|
||||
**search_notes Tests:**
|
||||
- Simple text queries
|
||||
- Tag-based searches
|
||||
- Boolean operators and complex queries
|
||||
- Empty/no results scenarios
|
||||
- Performance with growing knowledge base
|
||||
**Example Recent Changes (Update based on actual git log):**
|
||||
- Watch Service Restart (#156): Test project creation → file modification → automatic restart
|
||||
- Cross-Project Moves (#161): Test move_note with cross-project detection
|
||||
- Docker Environment Support (#174): Test BASIC_MEMORY_HOME behavior
|
||||
- MCP Server Logging (#164): Verify log level configurations
|
||||
|
||||
**Recent Activity Tests:**
|
||||
- Various timeframes ("today", "1 week", "1d")
|
||||
- Type filtering (if available)
|
||||
- Empty project scenarios
|
||||
- Performance with many recent changes
|
||||
### Phase 1: Core Functionality Validation (Tier 1 Tools)
|
||||
|
||||
**Context Building Tests:**
|
||||
- Different depth levels (1, 2, 3+)
|
||||
- Various timeframes
|
||||
- Relation traversal accuracy
|
||||
- Performance with complex graphs
|
||||
Test essential MCP tools that form the foundation of Basic Memory:
|
||||
|
||||
### Phase 2: v0.13.0 Feature Deep Dive
|
||||
**1. write_note Tests (Critical):**
|
||||
- ✅ Basic note creation with frontmatter
|
||||
- ✅ Special characters and Unicode in titles
|
||||
- ✅ Various content types (lists, headings, code blocks)
|
||||
- ✅ Empty notes and minimal content edge cases
|
||||
- ⚠️ Error handling for invalid parameters
|
||||
|
||||
**Project Management:**
|
||||
- Create multiple projects dynamically
|
||||
- Switch between projects mid-conversation
|
||||
- Cross-project operations
|
||||
- Project discovery and status
|
||||
- Default project behavior
|
||||
- Invalid project handling
|
||||
**2. read_note Tests (Critical):**
|
||||
- ✅ Read by title, permalink, memory:// URLs
|
||||
- ✅ Non-existent notes (error handling)
|
||||
- ✅ Notes with complex markdown formatting
|
||||
- ⚠️ Performance with large notes (>10MB)
|
||||
|
||||
**Advanced Note Editing:**
|
||||
- `edit_note` with append operations
|
||||
- Prepend operations
|
||||
- Find/replace with validation
|
||||
- Section replacement under headers
|
||||
- Error scenarios (invalid operations)
|
||||
- Frontmatter preservation
|
||||
**3. search_notes Tests (Critical):**
|
||||
- ✅ Simple text queries across content
|
||||
- ✅ Tag-based searches with multiple tags
|
||||
- ✅ Boolean operators (AND, OR, NOT)
|
||||
- ✅ Empty/no results scenarios
|
||||
- ⚠️ Performance with 100+ notes
|
||||
|
||||
**File Management:**
|
||||
- `move_note` within same project
|
||||
- Move between projects
|
||||
- Automatic folder creation
|
||||
- Special characters in paths
|
||||
- Database consistency validation
|
||||
- Search index updates after moves
|
||||
**4. edit_note Tests (Critical):**
|
||||
- ✅ Append operations preserving frontmatter
|
||||
- ✅ Prepend operations
|
||||
- ✅ Find/replace with validation
|
||||
- ✅ Section replacement under headers
|
||||
- ⚠️ Error scenarios (invalid operations)
|
||||
|
||||
### Phase 3: Edge Case Exploration
|
||||
**5. list_memory_projects Tests (Critical):**
|
||||
- ✅ Display all projects with clear session guidance
|
||||
- ✅ Project discovery workflow prompts
|
||||
- ✅ Removal of CLI-specific references
|
||||
- ✅ Empty project list handling
|
||||
- ✅ Single project constraint mode display
|
||||
|
||||
**6. recent_activity Tests (Critical - Discovery Mode):**
|
||||
- ✅ Discovery mode without project parameter
|
||||
- ✅ Intelligent project suggestions based on activity
|
||||
- ✅ Guidance prompts for project selection
|
||||
- ✅ Session tracking reminders in responses
|
||||
- ⚠️ Performance with multiple projects
|
||||
|
||||
### Phase 2: Important Workflows (Tier 2 Tools)
|
||||
|
||||
**7. build_context Tests (Important):**
|
||||
- ✅ Different depth levels (1, 2, 3+)
|
||||
- ✅ Various timeframes for context
|
||||
- ✅ memory:// URL navigation
|
||||
- ⚠️ Performance with complex relation graphs
|
||||
|
||||
**8. create_memory_project Tests (Important):**
|
||||
- ✅ Create projects dynamically
|
||||
- ✅ Set default during creation
|
||||
- ✅ Path validation and creation
|
||||
- ⚠️ Invalid paths and names
|
||||
- ✅ Integration with existing projects
|
||||
|
||||
**9. move_note Tests (Important):**
|
||||
- ✅ Move within same project
|
||||
- ✅ Cross-project moves with detection (#161)
|
||||
- ✅ Automatic folder creation
|
||||
- ✅ Database consistency validation
|
||||
- ⚠️ Special characters in paths
|
||||
|
||||
**10. sync_status Tests (Important):**
|
||||
- ✅ Background operation monitoring
|
||||
- ✅ File synchronization status
|
||||
- ✅ Project sync state reporting
|
||||
- ⚠️ Error state handling
|
||||
|
||||
### Phase 3: Enhanced Functionality (Tier 3 Tools)
|
||||
|
||||
**11. view_note Tests (Enhanced):**
|
||||
- ✅ Claude Desktop artifact display
|
||||
- ✅ Title extraction from frontmatter
|
||||
- ✅ Unicode and emoji content rendering
|
||||
- ⚠️ Error handling for non-existent notes
|
||||
|
||||
**12. read_content Tests (Enhanced):**
|
||||
- ✅ Raw file content access
|
||||
- ✅ Binary file handling
|
||||
- ✅ Image file reading
|
||||
- ⚠️ Large file performance
|
||||
|
||||
**13. delete_note Tests (Enhanced):**
|
||||
- ✅ Single note deletion
|
||||
- ✅ Database consistency after deletion
|
||||
- ⚠️ Non-existent note handling
|
||||
- ✅ Confirmation of successful deletion
|
||||
|
||||
**14. list_directory Tests (Enhanced):**
|
||||
- ✅ Directory content listing
|
||||
- ✅ Depth control and filtering
|
||||
- ✅ File name globbing
|
||||
- ⚠️ Empty directory handling
|
||||
|
||||
**15. delete_project Tests (Enhanced):**
|
||||
- ✅ Project removal from config
|
||||
- ✅ Database cleanup
|
||||
- ⚠️ Default project protection
|
||||
- ⚠️ Non-existent project handling
|
||||
|
||||
### Phase 4: Edge Case Exploration
|
||||
|
||||
**Boundary Testing:**
|
||||
- Very long titles and content (stress limits)
|
||||
@@ -134,7 +276,7 @@ Test all fundamental MCP tools systematically:
|
||||
- Rapid successive operations
|
||||
- Memory usage monitoring
|
||||
|
||||
### Phase 4: Real-World Workflow Scenarios
|
||||
### Phase 5: Real-World Workflow Scenarios
|
||||
|
||||
**Meeting Notes Pipeline:**
|
||||
1. Create meeting notes with action items
|
||||
@@ -154,7 +296,7 @@ Test all fundamental MCP tools systematically:
|
||||
1. Technical documentation project
|
||||
2. Personal recipe collection project
|
||||
3. Learning/course notes project
|
||||
4. Switch contexts during conversation
|
||||
4. Specify different projects for different operations
|
||||
5. Cross-reference related concepts
|
||||
|
||||
**Content Evolution:**
|
||||
@@ -164,7 +306,35 @@ Test all fundamental MCP tools systematically:
|
||||
4. Update content with edit operations
|
||||
5. Validate knowledge graph integrity
|
||||
|
||||
### Phase 5: Creative Stress Testing
|
||||
### Phase 6: Specialized Tools Testing (Tier 4)
|
||||
|
||||
**16. canvas Tests (Specialized):**
|
||||
- ✅ JSON Canvas generation
|
||||
- ✅ Node and edge creation
|
||||
- ✅ Obsidian compatibility
|
||||
- ⚠️ Complex graph handling
|
||||
|
||||
**17. MCP Prompts Tests (Specialized):**
|
||||
- ✅ ai_assistant_guide output
|
||||
- ✅ continue_conversation functionality
|
||||
- ✅ Formatted search results
|
||||
- ✅ Enhanced activity reports
|
||||
|
||||
### Phase 7: Integration & File Watching Tests
|
||||
|
||||
**File System Integration:**
|
||||
- ✅ Watch service behavior with file changes
|
||||
- ✅ Project creation → watch restart (#156)
|
||||
- ✅ Multi-project synchronization
|
||||
- ⚠️ MCP→API→DB→File stack validation
|
||||
|
||||
**Real Integration Testing:**
|
||||
- ✅ End-to-end file watching vs manual operations
|
||||
- ✅ Cross-session persistence
|
||||
- ✅ Database consistency across operations
|
||||
- ⚠️ Performance under real file system changes
|
||||
|
||||
### Phase 8: Creative Stress Testing
|
||||
|
||||
**Creative Exploration:**
|
||||
- Rapid project creation/switching patterns
|
||||
@@ -180,6 +350,26 @@ Test all fundamental MCP tools systematically:
|
||||
- Complex boolean search expressions
|
||||
- Resource constraint testing
|
||||
|
||||
## Test Execution Guidelines
|
||||
|
||||
### Quick Testing (core/features phases)
|
||||
- Focus on Tier 1 tools (core) or Tier 1+2 (features)
|
||||
- Test essential functionality and common edge cases
|
||||
- Record critical issues immediately
|
||||
- Complete in 15-20 minutes
|
||||
|
||||
### Comprehensive Testing (all phase)
|
||||
- Cover all tiers systematically
|
||||
- Include specialized tools and stress testing
|
||||
- Document performance baselines
|
||||
- Complete in 45-60 minutes
|
||||
|
||||
### Recent Changes Focus (recent phase)
|
||||
- Analyze git log for recent commits
|
||||
- Generate targeted test scenarios
|
||||
- Focus on regression testing for fixes
|
||||
- Validate new features thoroughly
|
||||
|
||||
## Test Observation Format
|
||||
|
||||
Record ALL observations immediately as Basic Memory notes:
|
||||
@@ -202,14 +392,14 @@ permalink: test-session-[phase]-[timestamp]
|
||||
## Test Results
|
||||
|
||||
### ✅ Successful Operations
|
||||
- [timestamp] write_note: Created note with emoji title 📝 #functionality
|
||||
- [timestamp] search_notes: Boolean query returned 23 results in 0.4s #performance
|
||||
- [timestamp] edit_note: Append operation preserved frontmatter #reliability
|
||||
- [timestamp] ✅ write_note: Created note with emoji title 📝 #tier1 #functionality
|
||||
- [timestamp] ✅ search_notes: Boolean query returned 23 results in 0.4s #tier1 #performance
|
||||
- [timestamp] ✅ edit_note: Append operation preserved frontmatter #tier1 #reliability
|
||||
|
||||
### ⚠️ Issues Discovered
|
||||
- [timestamp] move_note: Slow with deep folder paths (2.1s) #performance
|
||||
- [timestamp] search_notes: Unicode query returned unexpected results #bug
|
||||
- [timestamp] project switch: Context lost for memory:// URLs #issue
|
||||
- [timestamp] ⚠️ move_note: Slow with deep folder paths (2.1s) #tier2 #performance
|
||||
- [timestamp] 🚨 search_notes: Unicode query returned unexpected results #tier1 #bug #critical
|
||||
- [timestamp] ⚠️ build_context: Context lost for memory:// URLs #tier2 #issue
|
||||
|
||||
### 🚀 Enhancements Identified
|
||||
- edit_note could benefit from preview mode #ux-improvement
|
||||
@@ -219,7 +409,7 @@ permalink: test-session-[phase]-[timestamp]
|
||||
### 📊 Performance Metrics
|
||||
- Average write_note time: 0.3s
|
||||
- Search with 100+ notes: 0.6s
|
||||
- Project switch overhead: 0.1s
|
||||
- Project parameter overhead: <0.1s
|
||||
- Memory usage: [observed levels]
|
||||
|
||||
## Relations
|
||||
@@ -239,7 +429,7 @@ permalink: test-session-[phase]-[timestamp]
|
||||
- Learning curve and intuitiveness
|
||||
|
||||
**System Behavior:**
|
||||
- Context preservation across operations
|
||||
- Stateless operation independence
|
||||
- memory:// URL navigation reliability
|
||||
- Multi-step workflow cohesion
|
||||
- Edge case graceful handling
|
||||
@@ -353,24 +543,44 @@ For each error discovered:
|
||||
- Create comprehensive summary report
|
||||
- Generate development recommendations
|
||||
|
||||
## Testing Success Criteria
|
||||
|
||||
### Core Testing (Tier 1) - Must Pass
|
||||
- All 6 critical tools function correctly
|
||||
- No critical bugs in essential workflows
|
||||
- Acceptable performance for basic operations
|
||||
- Error handling works as expected
|
||||
|
||||
### Feature Testing (Tier 1+2) - Should Pass
|
||||
- All 11 core + important tools function
|
||||
- Workflow scenarios complete successfully
|
||||
- Performance meets baseline expectations
|
||||
- Integration points work correctly
|
||||
|
||||
### Comprehensive Testing (All Tiers) - Complete Coverage
|
||||
- All tools tested across all scenarios
|
||||
- Edge cases and stress testing completed
|
||||
- Performance baselines established
|
||||
- Full documentation of issues and enhancements
|
||||
|
||||
## Expected Outcomes
|
||||
|
||||
**System Validation:**
|
||||
- v0.13.0 feature verification in real usage
|
||||
- Edge case discovery beyond unit tests
|
||||
- Feature verification prioritized by tier importance
|
||||
- Recent changes validated for regression
|
||||
- Performance baseline establishment
|
||||
- Bug identification with reproduction cases
|
||||
- Bug identification with severity assessment
|
||||
|
||||
**Knowledge Base Creation:**
|
||||
- Comprehensive testing documentation
|
||||
- Prioritized testing documentation
|
||||
- Real usage examples for user guides
|
||||
- Edge case scenarios for future testing
|
||||
- Recent changes validation records
|
||||
- Performance insights for optimization
|
||||
|
||||
**Development Insights:**
|
||||
- Prioritized bug fix list
|
||||
- Tier-based bug priority list
|
||||
- Recent changes impact assessment
|
||||
- Enhancement ideas from real usage
|
||||
- Architecture validation results
|
||||
- User experience improvement areas
|
||||
|
||||
## Post-Test Deliverables
|
||||
@@ -402,9 +612,11 @@ For each error discovered:
|
||||
- Add performance benchmarks and targets
|
||||
|
||||
## Context
|
||||
- Uses installed basic-memory version (not development)
|
||||
- Uses real installed basic-memory version
|
||||
- Tests complete MCP→API→DB→File stack
|
||||
- Creates living documentation in Basic Memory itself
|
||||
- Follows integration over isolation philosophy
|
||||
- Prioritizes testing by tool importance and usage frequency
|
||||
- Adapts to recent development changes dynamically
|
||||
- Focuses on real usage patterns over checklist validation
|
||||
- Generates actionable insights for development team
|
||||
- Generates actionable insights prioritized by impact
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"basic-memory@basicmachines": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
# Git files
|
||||
.git/
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# Development files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Testing files
|
||||
tests/
|
||||
test-int/
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# Build artifacts
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
|
||||
# Virtual environments (uv creates these during build)
|
||||
.venv/
|
||||
venv/
|
||||
.env
|
||||
|
||||
# CI/CD files
|
||||
.github/
|
||||
|
||||
# Documentation (keep README.md and pyproject.toml)
|
||||
docs/
|
||||
CHANGELOG.md
|
||||
CLAUDE.md
|
||||
CONTRIBUTING.md
|
||||
|
||||
# Example files not needed for runtime
|
||||
examples/
|
||||
|
||||
# Local development files
|
||||
.basic-memory/
|
||||
*.db
|
||||
*.sqlite3
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
*.log
|
||||
@@ -0,0 +1,28 @@
|
||||
# Basic Memory Environment Variables Example
|
||||
# Copy this file to .env and customize as needed
|
||||
# Note: .env files are gitignored and should never be committed
|
||||
|
||||
# ============================================================================
|
||||
# PostgreSQL Test Database Configuration
|
||||
# ============================================================================
|
||||
# These variables allow you to override the default test database credentials
|
||||
# Default values match docker-compose-postgres.yml for local development
|
||||
#
|
||||
# Only needed if you want to use different credentials or a remote test database
|
||||
# By default, tests use: postgresql://basic_memory_user:dev_password@localhost:5433/basic_memory_test
|
||||
|
||||
# Full PostgreSQL test database URL (used by tests and migrations)
|
||||
# POSTGRES_TEST_URL=postgresql+asyncpg://basic_memory_user:dev_password@localhost:5433/basic_memory_test
|
||||
|
||||
# Individual components (used by justfile postgres-reset command)
|
||||
# POSTGRES_USER=basic_memory_user
|
||||
# POSTGRES_TEST_DB=basic_memory_test
|
||||
|
||||
# ============================================================================
|
||||
# Production Database Configuration
|
||||
# ============================================================================
|
||||
# For production use, set these in your deployment environment
|
||||
# DO NOT use the test credentials above in production!
|
||||
|
||||
# BASIC_MEMORY_DATABASE_BACKEND=postgres # or "sqlite"
|
||||
# BASIC_MEMORY_DATABASE_URL=postgresql+asyncpg://user:password@host:port/database
|
||||
@@ -1,55 +0,0 @@
|
||||
# OAuth Configuration for Basic Memory MCP Server
|
||||
# Copy this file to .env and update the values
|
||||
|
||||
# Enable OAuth authentication
|
||||
FASTMCP_AUTH_ENABLED=true
|
||||
|
||||
# OAuth provider type: basic, github, google, or supabase
|
||||
# - basic: Built-in OAuth provider with in-memory storage
|
||||
# - github: Integrate with GitHub OAuth
|
||||
# - google: Integrate with Google OAuth
|
||||
# - supabase: Integrate with Supabase Auth (recommended for production)
|
||||
FASTMCP_AUTH_PROVIDER=basic
|
||||
|
||||
# OAuth issuer URL (your MCP server URL)
|
||||
FASTMCP_AUTH_ISSUER_URL=http://localhost:8000
|
||||
|
||||
# Documentation URL for OAuth endpoints
|
||||
FASTMCP_AUTH_DOCS_URL=http://localhost:8000/docs/oauth
|
||||
|
||||
# Required scopes (comma-separated)
|
||||
# Examples: read,write,admin
|
||||
FASTMCP_AUTH_REQUIRED_SCOPES=read,write
|
||||
|
||||
# Secret key for JWT tokens (auto-generated if not set)
|
||||
# FASTMCP_AUTH_SECRET_KEY=your-secret-key-here
|
||||
|
||||
# Enable client registration endpoint
|
||||
FASTMCP_AUTH_CLIENT_REGISTRATION_ENABLED=true
|
||||
|
||||
# Enable token revocation endpoint
|
||||
FASTMCP_AUTH_REVOCATION_ENABLED=true
|
||||
|
||||
# Default scopes for new clients
|
||||
FASTMCP_AUTH_DEFAULT_SCOPES=read
|
||||
|
||||
# Valid scopes that can be requested
|
||||
FASTMCP_AUTH_VALID_SCOPES=read,write,admin
|
||||
|
||||
# Client secret expiry in seconds (optional)
|
||||
# FASTMCP_AUTH_CLIENT_SECRET_EXPIRY=86400
|
||||
|
||||
# GitHub OAuth settings (if using github provider)
|
||||
# GITHUB_CLIENT_ID=your-github-client-id
|
||||
# GITHUB_CLIENT_SECRET=your-github-client-secret
|
||||
|
||||
# Google OAuth settings (if using google provider)
|
||||
# GOOGLE_CLIENT_ID=your-google-client-id
|
||||
# GOOGLE_CLIENT_SECRET=your-google-client-secret
|
||||
|
||||
# Supabase settings (if using supabase provider)
|
||||
# SUPABASE_URL=https://your-project.supabase.co
|
||||
# SUPABASE_ANON_KEY=your-anon-key
|
||||
# SUPABASE_SERVICE_KEY=your-service-key # Optional, for admin operations
|
||||
# SUPABASE_JWT_SECRET=your-jwt-secret # Optional, for token validation
|
||||
# SUPABASE_ALLOWED_CLIENTS=client1,client2 # Comma-separated list of allowed client IDs
|
||||
@@ -0,0 +1,82 @@
|
||||
name: Claude Code Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
# Optional: Only run on specific file changes
|
||||
# paths:
|
||||
# - "src/**/*.ts"
|
||||
# - "src/**/*.tsx"
|
||||
# - "src/**/*.js"
|
||||
# - "src/**/*.jsx"
|
||||
|
||||
jobs:
|
||||
claude-review:
|
||||
# Only run for organization members and collaborators
|
||||
if: |
|
||||
github.event.pull_request.author_association == 'OWNER' ||
|
||||
github.event.pull_request.author_association == 'MEMBER' ||
|
||||
github.event.pull_request.author_association == 'COLLABORATOR'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code Review
|
||||
id: claude-review
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
track_progress: true # Enable visual progress tracking
|
||||
allowed_bots: '*'
|
||||
prompt: |
|
||||
Review this Basic Memory PR against our team checklist:
|
||||
|
||||
## Code Quality & Standards
|
||||
- [ ] Follows Basic Memory's coding conventions in CLAUDE.md
|
||||
- [ ] Python 3.12+ type annotations and async patterns
|
||||
- [ ] SQLAlchemy 2.0 best practices
|
||||
- [ ] FastAPI and Typer conventions followed
|
||||
- [ ] 100-character line length limit maintained
|
||||
- [ ] No commented-out code blocks
|
||||
|
||||
## Testing & Documentation
|
||||
- [ ] Unit tests for new functions/methods
|
||||
- [ ] Integration tests for new MCP tools
|
||||
- [ ] Test coverage for edge cases
|
||||
- [ ] Documentation updated (README, docstrings)
|
||||
- [ ] CLAUDE.md updated if conventions change
|
||||
|
||||
## Basic Memory Architecture
|
||||
- [ ] MCP tools follow atomic, composable design
|
||||
- [ ] Database changes include Alembic migrations
|
||||
- [ ] Preserves local-first architecture principles
|
||||
- [ ] Knowledge graph operations maintain consistency
|
||||
- [ ] Markdown file handling preserves integrity
|
||||
- [ ] AI-human collaboration patterns followed
|
||||
|
||||
## Security & Performance
|
||||
- [ ] No hardcoded secrets or credentials
|
||||
- [ ] Input validation for MCP tools
|
||||
- [ ] Proper error handling and logging
|
||||
- [ ] Performance considerations addressed
|
||||
- [ ] No sensitive data in logs or commits
|
||||
|
||||
## Compatability
|
||||
- [ ] File path comparisons must be windows compatible
|
||||
- [ ] Avoid using emojis and unicode characters in console and log output
|
||||
|
||||
Read the CLAUDE.md file for detailed project context. For each checklist item, verify if it's satisfied and comment on any that need attention. Use inline comments for specific code issues and post a summary with checklist results.
|
||||
|
||||
# Allow broader tool access for thorough code review
|
||||
claude_args: '--allowed-tools "Bash(gh pr:*),Bash(gh issue:*),Bash(gh api:*),Bash(git log:*),Bash(git show:*),Read,Grep,Glob"'
|
||||
@@ -0,0 +1,71 @@
|
||||
name: Claude Issue Triage
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Issue Triage
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
track_progress: true # Show triage progress
|
||||
prompt: |
|
||||
Analyze this new Basic Memory issue and perform triage:
|
||||
|
||||
**Issue Analysis:**
|
||||
1. **Type Classification:**
|
||||
- Bug report (code defect)
|
||||
- Feature request (new functionality)
|
||||
- Enhancement (improvement to existing feature)
|
||||
- Documentation (docs improvement)
|
||||
- Question/Support (user help)
|
||||
- MCP tool issue (specific to MCP functionality)
|
||||
|
||||
2. **Priority Assessment:**
|
||||
- Critical: Security issues, data loss, complete breakage
|
||||
- High: Major functionality broken, affects many users
|
||||
- Medium: Minor bugs, usability issues
|
||||
- Low: Nice-to-have improvements, cosmetic issues
|
||||
|
||||
3. **Component Classification:**
|
||||
- CLI commands
|
||||
- MCP tools
|
||||
- Database/sync
|
||||
- Cloud functionality
|
||||
- Documentation
|
||||
- Testing
|
||||
|
||||
4. **Complexity Estimate:**
|
||||
- Simple: Quick fix, documentation update
|
||||
- Medium: Requires some investigation/testing
|
||||
- Complex: Major feature work, architectural changes
|
||||
|
||||
**Actions to Take:**
|
||||
1. Add appropriate labels using: `gh issue edit ${{ github.event.issue.number }} --add-label "label1,label2"`
|
||||
2. Check for duplicates using: `gh search issues`
|
||||
3. If duplicate found, comment mentioning the original issue
|
||||
4. For feature requests, ask clarifying questions if needed
|
||||
5. For bugs, request reproduction steps if missing
|
||||
|
||||
**Available Labels:**
|
||||
- Type: bug, enhancement, feature, documentation, question, mcp-tool
|
||||
- Priority: critical, high, medium, low
|
||||
- Component: cli, mcp, database, cloud, docs, testing
|
||||
- Complexity: simple, medium, complex
|
||||
- Status: needs-reproduction, needs-clarification, duplicate
|
||||
|
||||
Read the issue carefully and provide helpful triage with appropriate labels.
|
||||
|
||||
claude_args: '--allowed-tools "Bash(gh issue:*),Bash(gh search:*),Read"'
|
||||
@@ -9,106 +9,60 @@ on:
|
||||
types: [opened, assigned]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
claude:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
||||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
||||
|
||||
(
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
||||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) ||
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.body, '@claude'))
|
||||
) && (
|
||||
github.event.comment.author_association == 'OWNER' ||
|
||||
github.event.comment.author_association == 'MEMBER' ||
|
||||
github.event.comment.author_association == 'COLLABORATOR' ||
|
||||
github.event.sender.author_association == 'OWNER' ||
|
||||
github.event.sender.author_association == 'MEMBER' ||
|
||||
github.event.sender.author_association == 'COLLABORATOR' ||
|
||||
github.event.pull_request.author_association == 'OWNER' ||
|
||||
github.event.pull_request.author_association == 'MEMBER' ||
|
||||
github.event.pull_request.author_association == 'COLLABORATOR'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
id-token: write
|
||||
actions: read # Required for Claude to read CI results on PRs
|
||||
steps:
|
||||
- name: Check user permissions
|
||||
id: check_membership
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
let actor;
|
||||
if (context.eventName === 'issue_comment') {
|
||||
actor = context.payload.comment.user.login;
|
||||
} else if (context.eventName === 'pull_request_review_comment') {
|
||||
actor = context.payload.comment.user.login;
|
||||
} else if (context.eventName === 'pull_request_review') {
|
||||
actor = context.payload.review.user.login;
|
||||
} else if (context.eventName === 'issues') {
|
||||
actor = context.payload.issue.user.login;
|
||||
}
|
||||
|
||||
console.log(`Checking permissions for user: ${actor}`);
|
||||
|
||||
// List of explicitly allowed users (organization members)
|
||||
const allowedUsers = [
|
||||
'phernandez',
|
||||
'groksrc',
|
||||
'nellins',
|
||||
'bm-claudeai'
|
||||
];
|
||||
|
||||
if (allowedUsers.includes(actor)) {
|
||||
console.log(`User ${actor} is in the allowed list`);
|
||||
core.setOutput('is_member', true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback: Check if user has repository permissions
|
||||
try {
|
||||
const collaboration = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username: actor
|
||||
});
|
||||
|
||||
const permission = collaboration.data.permission;
|
||||
console.log(`User ${actor} has permission level: ${permission}`);
|
||||
|
||||
// Allow if user has push access or higher (write, maintain, admin)
|
||||
const allowed = ['write', 'maintain', 'admin'].includes(permission);
|
||||
|
||||
core.setOutput('is_member', allowed);
|
||||
|
||||
if (!allowed) {
|
||||
core.notice(`User ${actor} does not have sufficient repository permissions (has: ${permission})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Error checking permissions: ${error.message}`);
|
||||
|
||||
// Final fallback: Check if user is a public member of the organization
|
||||
try {
|
||||
const membership = await github.rest.orgs.getMembershipForUser({
|
||||
org: 'basicmachines-co',
|
||||
username: actor
|
||||
});
|
||||
|
||||
const allowed = membership.data.state === 'active';
|
||||
core.setOutput('is_member', allowed);
|
||||
|
||||
if (!allowed) {
|
||||
core.notice(`User ${actor} is not a public member of basicmachines-co organization`);
|
||||
}
|
||||
} catch (membershipError) {
|
||||
console.log(`Error checking organization membership: ${membershipError.message}`);
|
||||
core.setOutput('is_member', false);
|
||||
core.notice(`User ${actor} does not have access to this repository`);
|
||||
}
|
||||
}
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.check_membership.outputs.is_member == 'true'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# For pull_request_target, checkout the PR head to review the actual changes
|
||||
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code
|
||||
if: steps.check_membership.outputs.is_member == 'true'
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@beta
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
allowed_tools: Bash(uv run pytest),Bash(uv run ruff check . --fix),Bash(uv run ruff format .),Bash(uv run pyright),Bash(just test),Bash(just lint),Bash(just format),Bash(just type-check),Bash(just check),Read,Write,Edit,MultiEdit,Glob,Grep,LS
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
track_progress: true # Enable visual progress tracking
|
||||
|
||||
# This is an optional setting that allows Claude to read CI results on PRs
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
|
||||
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
||||
# prompt: 'Update the pull request description to include a summary of changes.'
|
||||
|
||||
# Optional: Add claude_args to customize behavior and configuration
|
||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||
# or https://docs.claude.com/en/docs/claude-code/sdk#command-line for available options
|
||||
# claude_args: '--model claude-opus-4-1-20250805 --allowed-tools Bash(gh pr:*)'
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Docker Image CI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*' # Trigger on version tags like v1.0.0, v0.13.0, etc.
|
||||
workflow_dispatch: # Allow manual triggering for testing
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: basicmachines-co/basic-memory
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@@ -51,4 +51,35 @@ jobs:
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.PYPI_TOKEN }}
|
||||
password: ${{ secrets.PYPI_TOKEN }}
|
||||
|
||||
homebrew:
|
||||
name: Update Homebrew Formula
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
# Only run for stable releases (not dev, beta, or rc versions)
|
||||
if: ${{ !contains(github.ref_name, 'dev') && !contains(github.ref_name, 'b') && !contains(github.ref_name, 'rc') }}
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Update Homebrew formula
|
||||
uses: mislav/bump-homebrew-formula-action@v3
|
||||
with:
|
||||
# Formula name in homebrew-basic-memory repo
|
||||
formula-name: basic-memory
|
||||
# The tap repository
|
||||
homebrew-tap: basicmachines-co/homebrew-basic-memory
|
||||
# Base branch of the tap repository
|
||||
base-branch: main
|
||||
# Download URL will be automatically constructed from the tag
|
||||
download-url: https://github.com/basicmachines-co/basic-memory/archive/refs/tags/${{ github.ref_name }}.tar.gz
|
||||
# Commit message for the formula update
|
||||
commit-message: |
|
||||
{{formulaName}} {{version}}
|
||||
|
||||
Created by https://github.com/basicmachines-co/basic-memory/actions/runs/${{ github.run_id }}
|
||||
env:
|
||||
# Personal Access Token with repo scope for homebrew-basic-memory repo
|
||||
COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TOKEN }}
|
||||
|
||||
|
||||
@@ -13,12 +13,72 @@ on:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
test-sqlite:
|
||||
name: Test SQLite (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ "3.12" ]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
python-version: [ "3.12", "3.13" ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
pip install uv
|
||||
|
||||
- name: Install just (Linux/macOS)
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
|
||||
|
||||
- name: Install just (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
# Install just using Chocolatey (pre-installed on GitHub Actions Windows runners)
|
||||
choco install just --yes
|
||||
shell: pwsh
|
||||
|
||||
- name: Create virtual env
|
||||
run: |
|
||||
uv venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install -e .[dev]
|
||||
|
||||
- name: Run type checks
|
||||
run: |
|
||||
just typecheck
|
||||
|
||||
- name: Run linting
|
||||
run: |
|
||||
just lint
|
||||
|
||||
- name: Run tests (SQLite)
|
||||
run: |
|
||||
uv pip install pytest pytest-cov
|
||||
just test-sqlite
|
||||
|
||||
test-postgres:
|
||||
name: Test Postgres (Python ${{ matrix.python-version }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ "3.12", "3.13" ]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Note: No services section needed - testcontainers handles Postgres in Docker
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -47,11 +107,7 @@ jobs:
|
||||
run: |
|
||||
uv pip install -e .[dev]
|
||||
|
||||
- name: Run type checks
|
||||
run: |
|
||||
just type-check
|
||||
|
||||
- name: Run tests
|
||||
- name: Run tests (Postgres via testcontainers)
|
||||
run: |
|
||||
uv pip install pytest pytest-cov
|
||||
just test
|
||||
just test-postgres
|
||||
@@ -52,4 +52,5 @@ ENV/
|
||||
|
||||
# claude action
|
||||
claude-output
|
||||
**/.claude/settings.local.json
|
||||
**/.claude/settings.local.json
|
||||
.mcp.json
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"basic-memory": {
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"--directory",
|
||||
"/Users/phernandez/dev/basicmachines/basic-memory",
|
||||
"run",
|
||||
"src/basic_memory/cli/main.py",
|
||||
"mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
# OAuth Quick Start
|
||||
|
||||
Basic Memory supports OAuth authentication for secure access control. For detailed documentation, see [OAuth Authentication Guide](docs/OAuth%20Authentication%20Guide.md).
|
||||
|
||||
## Quick Test with MCP Inspector
|
||||
|
||||
```bash
|
||||
# 1. Set a consistent secret key
|
||||
export FASTMCP_AUTH_SECRET_KEY="test-secret-key"
|
||||
|
||||
# 2. Start server with OAuth
|
||||
FASTMCP_AUTH_ENABLED=true basic-memory mcp --transport streamable-http
|
||||
|
||||
# 3. In another terminal, get a test token
|
||||
export FASTMCP_AUTH_SECRET_KEY="test-secret-key" # Same key!
|
||||
basic-memory auth test-auth
|
||||
|
||||
# 4. Copy the access token and use in MCP Inspector:
|
||||
# - Server URL: http://localhost:8000/mcp
|
||||
# - Transport: streamable-http
|
||||
# - Custom Headers:
|
||||
# Authorization: Bearer YOUR_ACCESS_TOKEN
|
||||
# Accept: application/json, text/event-stream
|
||||
```
|
||||
|
||||
## OAuth Endpoints
|
||||
|
||||
- `GET /authorize` - Authorization endpoint
|
||||
- `POST /token` - Token exchange endpoint
|
||||
- `GET /.well-known/oauth-authorization-server` - OAuth metadata
|
||||
|
||||
## Common Issues
|
||||
|
||||
1. **401 Unauthorized**: Make sure you're using the same secret key for both server and client
|
||||
2. **404 Not Found**: Use `/authorize` not `/auth/authorize`
|
||||
3. **Token Invalid**: Tokens don't persist across server restarts with basic provider
|
||||
|
||||
## Documentation
|
||||
|
||||
- [OAuth Authentication Guide](docs/OAuth%20Authentication%20Guide.md) - Complete setup guide
|
||||
- [Supabase OAuth Setup](docs/Supabase%20OAuth%20Setup.md) - Production deployment
|
||||
- [External OAuth Providers](docs/External%20OAuth%20Providers.md) - GitHub, Google integration
|
||||
@@ -1,34 +1,71 @@
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
https://developercertificate.org/
|
||||
# Contributor License Agreement
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
## Copyright Assignment and License Grant
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
By signing this Contributor License Agreement ("Agreement"), you accept and agree to the following terms and conditions
|
||||
for your present and future Contributions submitted
|
||||
to Basic Machines LLC. Except for the license granted herein to Basic Machines LLC and recipients of software
|
||||
distributed by Basic Machines LLC, you reserve all right,
|
||||
title, and interest in and to your Contributions.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
### 1. Definitions
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this
|
||||
Agreement with Basic Machines LLC.
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work,
|
||||
that is intentionally submitted by You to Basic
|
||||
Machines LLC for inclusion in, or documentation of, any of the products owned or managed by Basic Machines LLC (the "
|
||||
Work").
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
### 2. Grant of Copyright License
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
Subject to the terms and conditions of this Agreement, You hereby grant to Basic Machines LLC and to recipients of
|
||||
software distributed by Basic Machines LLC a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the
|
||||
Work, and to permit persons to whom the Work is furnished to do so.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
### 3. Assignment of Copyright
|
||||
|
||||
You hereby assign to Basic Machines LLC all right, title, and interest worldwide in all Copyright covering your
|
||||
Contributions. Basic Machines LLC may license the
|
||||
Contributions under any license terms, including copyleft, permissive, commercial, or proprietary licenses.
|
||||
|
||||
### 4. Grant of Patent License
|
||||
|
||||
Subject to the terms and conditions of this Agreement, You hereby grant to Basic Machines LLC and to recipients of
|
||||
software distributed by Basic Machines LLC a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to
|
||||
make, have made, use, offer to sell, sell, import, and
|
||||
otherwise transfer the Work.
|
||||
|
||||
### 5. Developer Certificate of Origin
|
||||
|
||||
By making a Contribution to this project, You certify that:
|
||||
|
||||
(a) The Contribution was created in whole or in part by You and You have the right to submit it under this Agreement; or
|
||||
|
||||
(b) The Contribution is based upon previous work that, to the best of Your knowledge, is covered under an appropriate
|
||||
open source license and You have the right under that
|
||||
license to submit that work with modifications, whether created in whole or in part by You, under this Agreement; or
|
||||
|
||||
(c) The Contribution was provided directly to You by some other person who certified (a), (b) or (c) and You have not
|
||||
modified it.
|
||||
|
||||
(d) You understand and agree that this project and the Contribution are public and that a record of the Contribution (
|
||||
including all personal information You submit with
|
||||
it, including Your sign-off) is maintained indefinitely and may be redistributed consistent with this project or the
|
||||
open source license(s) involved.
|
||||
|
||||
### 6. Representations
|
||||
|
||||
You represent that you are legally entitled to grant the above license and assignment. If your employer(s) has rights to
|
||||
intellectual property that you create that
|
||||
includes your Contributions, you represent that you have received permission to make Contributions on behalf of that
|
||||
employer, or that your employer has waived such rights
|
||||
for your Contributions to Basic Machines LLC.
|
||||
|
||||
---
|
||||
|
||||
This Agreement is effective as of the date you first submit a Contribution to Basic Machines LLC.
|
||||
|
||||
@@ -15,19 +15,39 @@ See the [README.md](README.md) file for a project overview.
|
||||
### Build and Test Commands
|
||||
|
||||
- Install: `just install` or `pip install -e ".[dev]"`
|
||||
- Run tests: `uv run pytest -p pytest_mock -v` or `just test`
|
||||
- Run all tests (SQLite + Postgres): `just test`
|
||||
- Run all tests against SQLite: `just test-sqlite`
|
||||
- Run all tests against Postgres: `just test-postgres` (uses testcontainers)
|
||||
- Run unit tests (SQLite): `just test-unit-sqlite`
|
||||
- Run unit tests (Postgres): `just test-unit-postgres`
|
||||
- Run integration tests (SQLite): `just test-int-sqlite`
|
||||
- Run integration tests (Postgres): `just test-int-postgres`
|
||||
- Generate HTML coverage: `just coverage`
|
||||
- Single test: `pytest tests/path/to/test_file.py::test_function_name`
|
||||
- Run benchmarks: `pytest test-int/test_sync_performance_benchmark.py -v -m "benchmark and not slow"`
|
||||
- Lint: `just lint` or `ruff check . --fix`
|
||||
- Type check: `just type-check` or `uv run pyright`
|
||||
- Type check: `just typecheck` or `uv run pyright`
|
||||
- Format: `just format` or `uv run ruff format .`
|
||||
- Run all code checks: `just check` (runs lint, format, type-check, test)
|
||||
- Run all code checks: `just check` (runs lint, format, typecheck, test)
|
||||
- Create db migration: `just migration "Your migration message"`
|
||||
- Run development MCP Inspector: `just run-inspector`
|
||||
|
||||
**Note:** Project requires Python 3.12+ (uses type parameter syntax and `type` aliases introduced in 3.12)
|
||||
|
||||
**Postgres Testing:** Uses [testcontainers](https://testcontainers-python.readthedocs.io/) which automatically spins up a Postgres instance in Docker. No manual database setup required - just have Docker running.
|
||||
|
||||
### Test Structure
|
||||
|
||||
- `tests/` - Unit tests for individual components (mocked, fast)
|
||||
- `test-int/` - Integration tests for real-world scenarios (no mocks, realistic)
|
||||
- Both directories are covered by unified coverage reporting
|
||||
- Benchmark tests in `test-int/` are marked with `@pytest.mark.benchmark`
|
||||
- Slow tests are marked with `@pytest.mark.slow`
|
||||
|
||||
### Code Style Guidelines
|
||||
|
||||
- Line length: 100 characters max
|
||||
- Python 3.12+ with full type annotations
|
||||
- Python 3.12+ with full type annotations (uses type parameters and type aliases)
|
||||
- Format with ruff (consistent styling)
|
||||
- Import order: standard lib, third-party, local imports
|
||||
- Naming: snake_case for functions/variables, PascalCase for classes
|
||||
@@ -37,7 +57,6 @@ See the [README.md](README.md) file for a project overview.
|
||||
- API uses FastAPI for endpoints
|
||||
- Follow the repository pattern for data access
|
||||
- Tools communicate to api routers via the httpx ASGI client (in process)
|
||||
- avoid using "private" functions in modules or classes (prepended with _)
|
||||
|
||||
### Codebase Architecture
|
||||
|
||||
@@ -62,10 +81,48 @@ See the [README.md](README.md) file for a project overview.
|
||||
- Schema changes require Alembic migrations
|
||||
- SQLite is used for indexing and full text search, files are source of truth
|
||||
- Testing uses pytest with asyncio support (strict mode)
|
||||
- Test database uses in-memory SQLite
|
||||
- Avoid creating mocks in tests in most circumstances.
|
||||
- Each test runs in a standalone environment with in memory SQLite and tmp_file directory
|
||||
- Do not use mocks in tests if possible. Tests run with an in memory sqlite db, so they are not needed. See fixtures in conftest.py
|
||||
- Unit tests (`tests/`) use mocks when necessary; integration tests (`test-int/`) use real implementations
|
||||
- By default, tests run against SQLite (fast, no Docker needed)
|
||||
- Set `BASIC_MEMORY_TEST_POSTGRES=1` to run against Postgres (uses testcontainers - Docker required)
|
||||
- Each test runs in a standalone environment with isolated database and tmp_path directory
|
||||
- CI runs SQLite and Postgres tests in parallel for faster feedback
|
||||
- Performance benchmarks are in `test-int/test_sync_performance_benchmark.py`
|
||||
- Use pytest markers: `@pytest.mark.benchmark` for benchmarks, `@pytest.mark.slow` for slow tests
|
||||
|
||||
### Async Client Pattern (Important!)
|
||||
|
||||
**All MCP tools and CLI commands use the context manager pattern for HTTP clients:**
|
||||
|
||||
```python
|
||||
from basic_memory.mcp.async_client import get_client
|
||||
|
||||
async def my_mcp_tool():
|
||||
async with get_client() as client:
|
||||
# Use client for API calls
|
||||
response = await call_get(client, "/path")
|
||||
return response
|
||||
```
|
||||
|
||||
**Do NOT use:**
|
||||
- ❌ `from basic_memory.mcp.async_client import client` (deprecated module-level client)
|
||||
- ❌ Manual auth header management
|
||||
- ❌ `inject_auth_header()` (deleted)
|
||||
|
||||
**Key principles:**
|
||||
- Auth happens at client creation, not per-request
|
||||
- Proper resource management via context managers
|
||||
- Supports three modes: Local (ASGI), CLI cloud (HTTP + auth), Cloud app (factory injection)
|
||||
- Factory pattern enables dependency injection for cloud consolidation
|
||||
|
||||
**For cloud app integration:**
|
||||
```python
|
||||
from basic_memory.mcp import async_client
|
||||
|
||||
# Set custom factory before importing tools
|
||||
async_client.set_client_factory(your_custom_factory)
|
||||
```
|
||||
|
||||
See SPEC-16 for full context manager refactor details.
|
||||
|
||||
## BASIC MEMORY PRODUCT USAGE
|
||||
|
||||
@@ -82,6 +139,7 @@ See the [README.md](README.md) file for a project overview.
|
||||
|
||||
### Basic Memory Commands
|
||||
|
||||
**Local Commands:**
|
||||
- Sync knowledge: `basic-memory sync` or `basic-memory sync --watch`
|
||||
- Import from Claude: `basic-memory import claude conversations`
|
||||
- Import from ChatGPT: `basic-memory import chatgpt`
|
||||
@@ -91,24 +149,41 @@ See the [README.md](README.md) file for a project overview.
|
||||
- Guide: `basic-memory tools basic-memory-guide`
|
||||
- Continue: `basic-memory tools continue-conversation --topic="search"`
|
||||
|
||||
**Cloud Commands (requires subscription):**
|
||||
- Authenticate: `basic-memory cloud login`
|
||||
- Logout: `basic-memory cloud logout`
|
||||
- Bidirectional sync: `basic-memory cloud sync`
|
||||
- Integrity check: `basic-memory cloud check`
|
||||
- Mount cloud storage: `basic-memory cloud mount`
|
||||
- Unmount cloud storage: `basic-memory cloud unmount`
|
||||
|
||||
### MCP Capabilities
|
||||
|
||||
- Basic Memory exposes these MCP tools to LLMs:
|
||||
|
||||
**Content Management:**
|
||||
- `write_note(title, content, folder, tags)` - Create/update markdown notes with semantic observations and relations
|
||||
- `read_note(identifier, page, page_size)` - Read notes by title, permalink, or memory:// URL with knowledge graph
|
||||
awareness
|
||||
- `read_file(path)` - Read raw file content (text, images, binaries) without knowledge graph processing
|
||||
- `read_note(identifier, page, page_size)` - Read notes by title, permalink, or memory:// URL with knowledge graph awareness
|
||||
- `read_content(path)` - Read raw file content (text, images, binaries) without knowledge graph processing
|
||||
- `view_note(identifier, page, page_size)` - View notes as formatted artifacts for better readability
|
||||
- `edit_note(identifier, operation, content)` - Edit notes incrementally (append, prepend, find/replace, replace_section)
|
||||
- `move_note(identifier, destination_path)` - Move notes to new locations, updating database and maintaining links
|
||||
- `delete_note(identifier)` - Delete notes from the knowledge base
|
||||
|
||||
**Knowledge Graph Navigation:**
|
||||
- `build_context(url, depth, timeframe)` - Navigate the knowledge graph via memory:// URLs for conversation
|
||||
continuity
|
||||
- `recent_activity(type, depth, timeframe)` - Get recently updated information with specified timeframe (e.g., "
|
||||
1d", "1 week")
|
||||
- `build_context(url, depth, timeframe)` - Navigate the knowledge graph via memory:// URLs for conversation continuity
|
||||
- `recent_activity(type, depth, timeframe)` - Get recently updated information with specified timeframe (e.g., "1d", "1 week")
|
||||
- `list_directory(dir_name, depth, file_name_glob)` - Browse directory contents with filtering and depth control
|
||||
|
||||
**Search & Discovery:**
|
||||
- `search_notes(query, page, page_size)` - Full-text search across all content with filtering options
|
||||
- `search_notes(query, page, page_size, search_type, types, entity_types, after_date)` - Full-text search across all content with advanced filtering options
|
||||
|
||||
**Project Management:**
|
||||
- `list_memory_projects()` - List all available projects with their status
|
||||
- `create_memory_project(project_name, project_path, set_default)` - Create new Basic Memory projects
|
||||
- `delete_project(project_name)` - Delete a project from configuration
|
||||
- `get_current_project()` - Get current project information and stats
|
||||
- `sync_status()` - Check file synchronization and background operation status
|
||||
|
||||
**Visualization:**
|
||||
- `canvas(nodes, edges, title, folder)` - Generate Obsidian canvas files for knowledge graph visualization
|
||||
@@ -116,10 +191,38 @@ See the [README.md](README.md) file for a project overview.
|
||||
- MCP Prompts for better AI interaction:
|
||||
- `ai_assistant_guide()` - Guidance on effectively using Basic Memory tools for AI assistants
|
||||
- `continue_conversation(topic, timeframe)` - Continue previous conversations with relevant historical context
|
||||
- `search_notes(query, after_date)` - Search with detailed, formatted results for better context understanding
|
||||
- `search(query, after_date)` - Search with detailed, formatted results for better context understanding
|
||||
- `recent_activity(timeframe)` - View recently changed items with formatted output
|
||||
- `json_canvas_spec()` - Full JSON Canvas specification for Obsidian visualization
|
||||
|
||||
### Cloud Features (v0.15.0+)
|
||||
|
||||
Basic Memory now supports cloud synchronization and storage (requires active subscription):
|
||||
|
||||
**Authentication:**
|
||||
- JWT-based authentication with subscription validation
|
||||
- Secure session management with token refresh
|
||||
- Support for multiple cloud projects
|
||||
|
||||
**Bidirectional Sync:**
|
||||
- rclone bisync integration for two-way synchronization
|
||||
- Conflict resolution and integrity verification
|
||||
- Real-time sync with change detection
|
||||
- Mount/unmount cloud storage for direct file access
|
||||
|
||||
**Cloud Project Management:**
|
||||
- Create and manage projects in the cloud
|
||||
- Toggle between local and cloud modes
|
||||
- Per-project sync configuration
|
||||
- Subscription-based access control
|
||||
|
||||
**Security & Performance:**
|
||||
- Removed .env file loading for improved security
|
||||
- .gitignore integration (respects gitignored files)
|
||||
- WAL mode for SQLite performance
|
||||
- Background relation resolution (non-blocking startup)
|
||||
- API performance optimizations (SPEC-11)
|
||||
|
||||
## AI-Human Collaborative Development
|
||||
|
||||
Basic Memory emerged from and enables a new kind of development process that combines human and AI capabilities. Instead
|
||||
@@ -134,34 +237,37 @@ of using AI just for code generation, we've developed a true collaborative workf
|
||||
This approach has allowed us to tackle more complex challenges and build a more robust system than either humans or AI
|
||||
could achieve independently.
|
||||
|
||||
**Problem-Solving Guidance:**
|
||||
- If a solution isn't working after reasonable effort, suggest alternative approaches
|
||||
- Don't persist with a problematic library or pattern when better alternatives exist
|
||||
- Example: When py-pglite caused cascading test failures, switching to testcontainers-postgres was the right call
|
||||
|
||||
## GitHub Integration
|
||||
|
||||
Basic Memory uses Claude directly into the development workflow through GitHub:
|
||||
Basic Memory has taken AI-Human collaboration to the next level by integrating Claude directly into the development workflow through GitHub:
|
||||
|
||||
### GitHub MCP Tools
|
||||
|
||||
Using the GitHub Model Context Protocol server, Claude can:
|
||||
Using the GitHub Model Context Protocol server, Claude can now:
|
||||
|
||||
- **Repository Management**:
|
||||
- View repository files and structure
|
||||
- Read file contents
|
||||
- Create new branches
|
||||
- Create and update files
|
||||
- View repository files and structure
|
||||
- Read file contents
|
||||
- Create new branches
|
||||
- Create and update files
|
||||
|
||||
- **Issue Management**:
|
||||
- Create new issues
|
||||
- Comment on existing issues
|
||||
- Close and update issues
|
||||
- Search across issues
|
||||
- Create new issues
|
||||
- Comment on existing issues
|
||||
- Close and update issues
|
||||
- Search across issues
|
||||
|
||||
- **Pull Request Workflow**:
|
||||
- Create pull requests
|
||||
- Review code changes
|
||||
- Add comments to PRs
|
||||
- Create pull requests
|
||||
- Review code changes
|
||||
- Add comments to PRs
|
||||
|
||||
This integration enables Claude to participate as a full team member in the development process, not just as a code
|
||||
generation tool. Claude's GitHub account ([bm-claudeai](https://github.com/bm-claudeai)) is a member of the Basic
|
||||
Machines organization with direct contributor access to the codebase.
|
||||
This integration enables Claude to participate as a full team member in the development process, not just as a code generation tool. Claude's GitHub account ([bm-claudeai](https://github.com/bm-claudeai)) is a member of the Basic Machines organization with direct contributor access to the codebase.
|
||||
|
||||
### Collaborative Development Process
|
||||
|
||||
@@ -171,54 +277,6 @@ With GitHub integration, the development workflow includes:
|
||||
2. **Contribution tracking** - All of Claude's contributions are properly attributed in the Git history
|
||||
3. **Branch management** - Claude can create feature branches for implementations
|
||||
4. **Documentation maintenance** - Claude can keep documentation updated as the code evolves
|
||||
5. **Code Commits**: ALWAYS sign off commits with `git commit -s`
|
||||
|
||||
With this integration, the AI assistant is a full-fledged team member rather than just a tool for generating code
|
||||
snippets.
|
||||
|
||||
|
||||
### Basic Memory Pro
|
||||
|
||||
Basic Memory Pro is a desktop GUI application that wraps the basic-memory CLI/MCP tools:
|
||||
|
||||
- Built with Tauri (Rust), React (TypeScript), and a Python FastAPI sidecar
|
||||
- Provides visual knowledge graph exploration and project management
|
||||
- Uses the same core codebase but adds a desktop-friendly interface
|
||||
- Project configuration is shared between CLI and Pro versions
|
||||
- Multiple project support with visual switching interface
|
||||
|
||||
local repo: /Users/phernandez/dev/basicmachines/basic-memory-pro
|
||||
github: https://github.com/basicmachines-co/basic-memory-pro
|
||||
|
||||
## Release and Version Management
|
||||
|
||||
Basic Memory uses `uv-dynamic-versioning` for automatic version management based on git tags:
|
||||
|
||||
### Version Types
|
||||
- **Development versions**: Automatically generated from commits (e.g., `0.12.4.dev26+468a22f`)
|
||||
- **Beta releases**: Created by tagging with beta suffixes (e.g., `v0.13.0b1`, `v0.13.0rc1`)
|
||||
- **Stable releases**: Created by tagging with version numbers (e.g., `v0.13.0`)
|
||||
|
||||
### Release Workflows
|
||||
|
||||
#### Development Builds (Automatic)
|
||||
- Triggered on every push to `main` branch
|
||||
- Publishes dev versions like `0.12.4.dev26+468a22f` to PyPI
|
||||
- Allows continuous testing of latest changes
|
||||
- Users install with: `pip install basic-memory --pre --force-reinstall`
|
||||
|
||||
#### Beta/RC Releases (Manual)
|
||||
- Create beta tag: `git tag v0.13.0b1 && git push origin v0.13.0b1`
|
||||
- Automatically builds and publishes to PyPI as pre-release
|
||||
- Users install with: `pip install basic-memory --pre`
|
||||
- Use for milestone testing before stable release
|
||||
|
||||
#### Stable Releases (Manual)
|
||||
- Create version tag: `git tag v0.13.0 && git push origin v0.13.0`
|
||||
- Automatically builds, creates GitHub release, and publishes to PyPI
|
||||
- Users install with: `pip install basic-memory`
|
||||
|
||||
### For Development
|
||||
- No manual version bumping required
|
||||
- Versions automatically derived from git tags
|
||||
- `pyproject.toml` uses `dynamic = ["version"]`
|
||||
- `__init__.py` dynamically reads version from package metadata
|
||||
This level of integration represents a new paradigm in AI-human collaboration, where the AI assistant becomes a full-fledged team member rather than just a tool for generating code snippets.
|
||||
|
||||
@@ -27,13 +27,25 @@ project and how to get started as a developer.
|
||||
|
||||
> **Note**: Basic Memory uses [just](https://just.systems) as a modern command runner. Install with `brew install just` or `cargo install just`.
|
||||
|
||||
3. **Run the Tests**:
|
||||
3. **Activate the Virtual Environment**
|
||||
```bash
|
||||
# Run all tests
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
4. **Run the Tests**:
|
||||
```bash
|
||||
# Run all tests with unified coverage (unit + integration)
|
||||
just test
|
||||
# or
|
||||
uv run pytest -p pytest_mock -v
|
||||
|
||||
|
||||
# Run unit tests only (fast, no coverage)
|
||||
just test-unit
|
||||
|
||||
# Run integration tests only (fast, no coverage)
|
||||
just test-int
|
||||
|
||||
# Generate HTML coverage report
|
||||
just coverage
|
||||
|
||||
# Run a specific test
|
||||
pytest tests/path/to/test_file.py::test_function_name
|
||||
```
|
||||
@@ -129,7 +141,7 @@ agreement to the DCO.
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
- **Python Version**: Python 3.12+ with full type annotations
|
||||
- **Python Version**: Python 3.12+ with full type annotations (3.12+ required for type parameter syntax)
|
||||
- **Line Length**: 100 characters maximum
|
||||
- **Formatting**: Use ruff for consistent styling
|
||||
- **Import Order**: Standard lib, third-party, local imports
|
||||
@@ -139,12 +151,78 @@ agreement to the DCO.
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
- **Coverage Target**: We aim for 100% test coverage for all code
|
||||
### Test Structure
|
||||
|
||||
Basic Memory uses two test directories with unified coverage reporting:
|
||||
|
||||
- **`tests/`**: Unit tests that test individual components in isolation
|
||||
- Fast execution with extensive mocking
|
||||
- Test individual functions, classes, and modules
|
||||
- Run with: `just test-unit` (no coverage, fast)
|
||||
|
||||
- **`test-int/`**: Integration tests that test real-world scenarios
|
||||
- Test full workflows with real database and file operations
|
||||
- Include performance benchmarks
|
||||
- More realistic but slower than unit tests
|
||||
- Run with: `just test-int` (no coverage, fast)
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests with unified coverage report
|
||||
just test
|
||||
|
||||
# Run only unit tests (fast iteration)
|
||||
just test-unit
|
||||
|
||||
# Run only integration tests
|
||||
just test-int
|
||||
|
||||
# Generate HTML coverage report
|
||||
just coverage
|
||||
|
||||
# Run specific test
|
||||
pytest tests/path/to/test_file.py::test_function_name
|
||||
|
||||
# Run tests excluding benchmarks
|
||||
pytest -m "not benchmark"
|
||||
|
||||
# Run only benchmark tests
|
||||
pytest -m benchmark test-int/test_sync_performance_benchmark.py
|
||||
```
|
||||
|
||||
### Performance Benchmarks
|
||||
|
||||
The `test-int/test_sync_performance_benchmark.py` file contains performance benchmarks that measure sync and indexing speed:
|
||||
|
||||
- `test_benchmark_sync_100_files` - Small repository performance
|
||||
- `test_benchmark_sync_500_files` - Medium repository performance
|
||||
- `test_benchmark_sync_1000_files` - Large repository performance (marked slow)
|
||||
- `test_benchmark_resync_no_changes` - Re-sync performance baseline
|
||||
|
||||
Run benchmarks with:
|
||||
```bash
|
||||
# Run all benchmarks (excluding slow ones)
|
||||
pytest test-int/test_sync_performance_benchmark.py -v -m "benchmark and not slow"
|
||||
|
||||
# Run all benchmarks including slow ones
|
||||
pytest test-int/test_sync_performance_benchmark.py -v -m benchmark
|
||||
|
||||
# Run specific benchmark
|
||||
pytest test-int/test_sync_performance_benchmark.py::test_benchmark_sync_100_files -v
|
||||
```
|
||||
|
||||
See `test-int/BENCHMARKS.md` for detailed benchmark documentation.
|
||||
|
||||
### Testing Best Practices
|
||||
|
||||
- **Coverage Target**: We aim for high test coverage for all code
|
||||
- **Test Framework**: Use pytest for unit and integration tests
|
||||
- **Mocking**: Use pytest-mock for mocking dependencies only when necessary
|
||||
- **Mocking**: Avoid mocking in integration tests; use sparingly in unit tests
|
||||
- **Edge Cases**: Test both normal operation and edge cases
|
||||
- **Database Testing**: Use in-memory SQLite for testing database operations
|
||||
- **Fixtures**: Use async pytest fixtures for setup and teardown
|
||||
- **Markers**: Use `@pytest.mark.benchmark` for benchmarks, `@pytest.mark.slow` for slow tests
|
||||
|
||||
## Release Process
|
||||
|
||||
|
||||
@@ -1,16 +1,46 @@
|
||||
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
|
||||
FROM python:3.12-slim
|
||||
FROM python:3.12-slim-bookworm
|
||||
|
||||
# Build arguments for user ID and group ID (defaults to 1000)
|
||||
ARG UID=1000
|
||||
ARG GID=1000
|
||||
|
||||
# Copy uv from official image
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# Create a group and user with the provided UID/GID
|
||||
# Check if the GID already exists, if not create appgroup
|
||||
RUN (getent group ${GID} || groupadd --gid ${GID} appgroup) && \
|
||||
useradd --uid ${UID} --gid ${GID} --create-home --shell /bin/bash appuser
|
||||
|
||||
# Copy the project into the image
|
||||
ADD . /app
|
||||
|
||||
# Sync the project into a new environment, asserting the lockfile is up to date
|
||||
WORKDIR /app
|
||||
RUN uv sync --locked
|
||||
|
||||
# Copy the project files
|
||||
COPY . .
|
||||
# Create necessary directories and set ownership
|
||||
RUN mkdir -p /app/data/basic-memory /app/.basic-memory && \
|
||||
chown -R appuser:${GID} /app
|
||||
|
||||
# Install pip and build dependencies
|
||||
RUN pip install --upgrade pip \
|
||||
&& pip install . --no-cache-dir --ignore-installed
|
||||
# Set default data directory and add venv to PATH
|
||||
ENV BASIC_MEMORY_HOME=/app/data/basic-memory \
|
||||
BASIC_MEMORY_PROJECT_ROOT=/app/data \
|
||||
PATH="/app/.venv/bin:$PATH"
|
||||
|
||||
# Expose port if necessary (e.g., uv might use a port, but MCP over stdio so not needed here)
|
||||
# Switch to the non-root user
|
||||
USER appuser
|
||||
|
||||
# Use the basic-memory entrypoint to run the MCP server
|
||||
CMD ["basic-memory", "mcp"]
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD basic-memory --version || exit 1
|
||||
|
||||
# Use the basic-memory entrypoint to run the MCP server with default SSE transport
|
||||
CMD ["basic-memory", "mcp", "--transport", "sse", "--host", "0.0.0.0", "--port", "8000"]
|
||||
@@ -7,6 +7,16 @@
|
||||

|
||||
[](https://smithery.ai/server/@basicmachines-co/basic-memory)
|
||||
|
||||
## 🚀 Basic Memory Cloud is Live!
|
||||
|
||||
- **Cross-device and multi-platform support is here.** Your knowledge graph now works on desktop, web, and mobile - seamlessly synced across all your AI tools (Claude, ChatGPT, Gemini, Claude Code, and Codex)
|
||||
- **Early Supporter Pricing:** Early users get 25% off forever.
|
||||
The open source project continues as always. Cloud just makes it work everywhere.
|
||||
|
||||
[Sign up now →](https://basicmemory.com/beta)
|
||||
|
||||
with a 7 day free trial
|
||||
|
||||
# Basic Memory
|
||||
|
||||
Basic Memory lets you build persistent knowledge through natural conversations with Large Language Models (LLMs) like
|
||||
@@ -100,6 +110,9 @@ With Basic Memory, you can:
|
||||
- Keep everything local and under your control
|
||||
- Use familiar tools like Obsidian to view and edit notes
|
||||
- Build a personal knowledge base that grows over time
|
||||
- Sync your knowledge to the cloud with bidirectional synchronization
|
||||
- Authenticate and manage cloud projects with subscription validation
|
||||
- Mount cloud storage for direct file access
|
||||
|
||||
## How It Works in Practice
|
||||
|
||||
@@ -153,7 +166,7 @@ The note embeds semantic content and links to other topics via simple Markdown f
|
||||
|
||||
3. You see this file on your computer in real time in the current project directory (default `~/$HOME/basic-memory`).
|
||||
|
||||
- Realtime sync is enabled by default with the v0.12.0 version
|
||||
- Realtime sync can be enabled via running `basic-memory sync --watch`
|
||||
|
||||
4. In a chat with the LLM, you can reference a topic:
|
||||
|
||||
@@ -263,13 +276,6 @@ Examples of relations:
|
||||
```
|
||||
|
||||
## Using with VS Code
|
||||
For one-click installation, click one of the install buttons below...
|
||||
|
||||
[](https://insiders.vscode.dev/redirect/mcp/install?name=basic-memory&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22basic-memory%22%2C%22mcp%22%5D%7D) [](https://insiders.vscode.dev/redirect/mcp/install?name=basic-memory&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22basic-memory%22%2C%22mcp%22%5D%7D&quality=insiders)
|
||||
|
||||
You can use Basic Memory with VS Code to easily retrieve and store information while coding. Click the installation buttons above for one-click setup, or follow the manual installation instructions below.
|
||||
|
||||
### Manual Installation
|
||||
|
||||
Add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` and typing `Preferences: Open User Settings (JSON)`.
|
||||
|
||||
@@ -299,6 +305,8 @@ Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace
|
||||
}
|
||||
```
|
||||
|
||||
You can use Basic Memory with VS Code to easily retrieve and store information while coding.
|
||||
|
||||
## Using with Claude Desktop
|
||||
|
||||
Basic Memory is built using the MCP (Model Context Protocol) and works with the Claude desktop app (https://claude.ai/):
|
||||
@@ -322,8 +330,7 @@ for OS X):
|
||||
}
|
||||
```
|
||||
|
||||
If you want to use a specific project (see [Multiple Projects](docs/User%20Guide.md#multiple-projects)), update your
|
||||
Claude Desktop
|
||||
If you want to use a specific project (see [Multiple Projects](#multiple-projects) below), update your Claude Desktop
|
||||
config:
|
||||
|
||||
```json
|
||||
@@ -333,9 +340,9 @@ config:
|
||||
"command": "uvx",
|
||||
"args": [
|
||||
"basic-memory",
|
||||
"mcp",
|
||||
"--project",
|
||||
"your-project-name",
|
||||
"mcp"
|
||||
"your-project-name"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -344,16 +351,65 @@ config:
|
||||
|
||||
2. Sync your knowledge:
|
||||
|
||||
Basic Memory will sync the files in your project in real time if you make manual edits.
|
||||
```bash
|
||||
# One-time sync of local knowledge updates
|
||||
basic-memory sync
|
||||
|
||||
3. In Claude Desktop, the LLM can now use these tools:
|
||||
# Run realtime sync process (recommended)
|
||||
basic-memory sync --watch
|
||||
```
|
||||
|
||||
3. Cloud features (optional, requires subscription):
|
||||
|
||||
```bash
|
||||
# Authenticate with cloud
|
||||
basic-memory cloud login
|
||||
|
||||
# Bidirectional sync with cloud
|
||||
basic-memory cloud sync
|
||||
|
||||
# Verify cloud integrity
|
||||
basic-memory cloud check
|
||||
|
||||
# Mount cloud storage
|
||||
basic-memory cloud mount
|
||||
```
|
||||
|
||||
4. In Claude Desktop, the LLM can now use these tools:
|
||||
|
||||
**Content Management:**
|
||||
```
|
||||
write_note(title, content, folder, tags) - Create or update notes
|
||||
read_note(identifier, page, page_size) - Read notes by title or permalink
|
||||
read_content(path) - Read raw file content (text, images, binaries)
|
||||
view_note(identifier) - View notes as formatted artifacts
|
||||
edit_note(identifier, operation, content) - Edit notes incrementally
|
||||
move_note(identifier, destination_path) - Move notes with database consistency
|
||||
delete_note(identifier) - Delete notes from knowledge base
|
||||
```
|
||||
|
||||
**Knowledge Graph Navigation:**
|
||||
```
|
||||
build_context(url, depth, timeframe) - Navigate knowledge graph via memory:// URLs
|
||||
search_notes(query, page, page_size) - Search across your knowledge base
|
||||
recent_activity(type, depth, timeframe) - Find recently updated information
|
||||
list_directory(dir_name, depth) - Browse directory contents with filtering
|
||||
```
|
||||
|
||||
**Search & Discovery:**
|
||||
```
|
||||
search(query, page, page_size) - Search across your knowledge base
|
||||
```
|
||||
|
||||
**Project Management:**
|
||||
```
|
||||
list_memory_projects() - List all available projects
|
||||
create_memory_project(project_name, project_path) - Create new projects
|
||||
get_current_project() - Show current project stats
|
||||
sync_status() - Check synchronization status
|
||||
```
|
||||
|
||||
**Visualization:**
|
||||
```
|
||||
canvas(nodes, edges, title, folder) - Generate knowledge visualizations
|
||||
```
|
||||
|
||||
@@ -371,29 +427,97 @@ canvas(nodes, edges, title, folder) - Generate knowledge visualizations
|
||||
|
||||
See the [Documentation](https://memory.basicmachines.co/) for more info, including:
|
||||
|
||||
- [Complete User Guide](https://memory.basicmachines.co/docs/user-guide)
|
||||
- [CLI tools](https://memory.basicmachines.co/docs/cli-reference)
|
||||
- [Managing multiple Projects](https://memory.basicmachines.co/docs/cli-reference#project)
|
||||
- [Importing data from OpenAI/Claude Projects](https://memory.basicmachines.co/docs/cli-reference#import)
|
||||
- [Complete User Guide](https://docs.basicmemory.com/user-guide/)
|
||||
- [CLI tools](https://docs.basicmemory.com/guides/cli-reference/)
|
||||
- [Cloud CLI and Sync](https://docs.basicmemory.com/guides/cloud-cli/)
|
||||
- [Managing multiple Projects](https://docs.basicmemory.com/guides/cli-reference/#project)
|
||||
- [Importing data from OpenAI/Claude Projects](https://docs.basicmemory.com/guides/cli-reference/#import)
|
||||
|
||||
## Installation Options
|
||||
## Logging
|
||||
|
||||
Basic Memory uses [Loguru](https://github.com/Delgan/loguru) for logging. The logging behavior varies by entry point:
|
||||
|
||||
| Entry Point | Default Behavior | Use Case |
|
||||
|-------------|------------------|----------|
|
||||
| CLI commands | File only | Prevents log output from interfering with command output |
|
||||
| MCP server | File only | Stdout would corrupt the JSON-RPC protocol |
|
||||
| API server | File (local) or stdout (cloud) | Docker/cloud deployments use stdout |
|
||||
|
||||
**Log file location:** `~/.basic-memory/basic-memory.log` (10MB rotation, 10 days retention)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `BASIC_MEMORY_LOG_LEVEL` | `INFO` | Log level: DEBUG, INFO, WARNING, ERROR |
|
||||
| `BASIC_MEMORY_CLOUD_MODE` | `false` | When `true`, API logs to stdout with structured context |
|
||||
| `BASIC_MEMORY_ENV` | `dev` | Set to `test` for test mode (stderr only) |
|
||||
|
||||
### Examples
|
||||
|
||||
### Stable Release
|
||||
```bash
|
||||
pip install basic-memory
|
||||
# Enable debug logging
|
||||
BASIC_MEMORY_LOG_LEVEL=DEBUG basic-memory sync
|
||||
|
||||
# View logs
|
||||
tail -f ~/.basic-memory/basic-memory.log
|
||||
|
||||
# Cloud/Docker mode (stdout logging with structured context)
|
||||
BASIC_MEMORY_CLOUD_MODE=true uvicorn basic_memory.api.app:app
|
||||
```
|
||||
|
||||
### Beta/Pre-releases
|
||||
## Development
|
||||
|
||||
### Running Tests
|
||||
|
||||
Basic Memory supports dual database backends (SQLite and Postgres). By default, tests run against SQLite. Set `BASIC_MEMORY_TEST_POSTGRES=1` to run against Postgres (uses testcontainers - Docker required).
|
||||
|
||||
**Quick Start:**
|
||||
```bash
|
||||
pip install basic-memory --pre
|
||||
# Run all tests against SQLite (default, fast)
|
||||
just test-sqlite
|
||||
|
||||
# Run all tests against Postgres (uses testcontainers)
|
||||
just test-postgres
|
||||
|
||||
# Run both SQLite and Postgres tests
|
||||
just test
|
||||
```
|
||||
|
||||
### Development Builds
|
||||
Development versions are automatically published on every commit to main with versions like `0.12.4.dev26+468a22f`:
|
||||
**Available Test Commands:**
|
||||
|
||||
- `just test` - Run all tests against both SQLite and Postgres
|
||||
- `just test-sqlite` - Run all tests against SQLite (fast, no Docker needed)
|
||||
- `just test-postgres` - Run all tests against Postgres (uses testcontainers)
|
||||
- `just test-unit-sqlite` - Run unit tests against SQLite
|
||||
- `just test-unit-postgres` - Run unit tests against Postgres
|
||||
- `just test-int-sqlite` - Run integration tests against SQLite
|
||||
- `just test-int-postgres` - Run integration tests against Postgres
|
||||
- `just test-windows` - Run Windows-specific tests (auto-skips on other platforms)
|
||||
- `just test-benchmark` - Run performance benchmark tests
|
||||
|
||||
**Postgres Testing:**
|
||||
|
||||
Postgres tests use [testcontainers](https://testcontainers-python.readthedocs.io/) which automatically spins up a Postgres instance in Docker. No manual database setup required - just have Docker running.
|
||||
|
||||
**Test Markers:**
|
||||
|
||||
Tests use pytest markers for selective execution:
|
||||
- `windows` - Windows-specific database optimizations
|
||||
- `benchmark` - Performance tests (excluded from default runs)
|
||||
|
||||
**Other Development Commands:**
|
||||
```bash
|
||||
pip install basic-memory --pre --force-reinstall
|
||||
just install # Install with dev dependencies
|
||||
just lint # Run linting checks
|
||||
just typecheck # Run type checking
|
||||
just format # Format code with ruff
|
||||
just check # Run all quality checks
|
||||
just migration "msg" # Create database migration
|
||||
```
|
||||
|
||||
See the [justfile](justfile) for the complete list of development commands.
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
# Release Notes v0.13.0
|
||||
|
||||
## Overview
|
||||
|
||||
Basic Memory v0.13.0 is a **major release** that transforms Basic Memory into a true multi-project knowledge management system. This release introduces fluid project switching, advanced note editing capabilities, robust file management, and production-ready OAuth authentication - all while maintaining full backward compatibility.
|
||||
|
||||
**What's New for Users:**
|
||||
- 🎯 **Switch between projects instantly** during conversations with Claude
|
||||
- ✏️ **Edit notes incrementally** without rewriting entire documents
|
||||
- 📁 **Move and organize notes** with full database consistency
|
||||
- 📖 **View notes as formatted artifacts** for better readability in Claude Desktop
|
||||
- 🔍 **Search frontmatter tags** to discover content more easily
|
||||
- 🔐 **OAuth authentication** for secure remote access
|
||||
- ⚡ **Development builds** automatically published for beta testing
|
||||
|
||||
**Key v0.13.0 Accomplishments:**
|
||||
- ✅ **Complete Project Management System** - Project switching and project-specific operations
|
||||
- ✅ **Advanced Note Editing** - Incremental editing with append, prepend, find/replace, and section operations
|
||||
- ✅ **View Notes as Artifacts in Claude Desktop/Web** - Use the view_note tool to view a note as an artifact
|
||||
- ✅ **File Management System** - Full move operations with database consistency and rollback protection
|
||||
- ✅ **Enhanced Search Capabilities** - Frontmatter tags now searchable, improved content discoverability
|
||||
- ✅ **Unified Database Architecture** - Single app-level database for better performance and project management
|
||||
|
||||
## Major Features
|
||||
|
||||
### 1. Multiple Project Management 🎯
|
||||
|
||||
**Switch between projects instantly during conversations:**
|
||||
|
||||
```
|
||||
💬 "What projects do I have?"
|
||||
🤖 Available projects:
|
||||
• main (current, default)
|
||||
• work-notes
|
||||
• personal-journal
|
||||
• code-snippets
|
||||
|
||||
💬 "Switch to work-notes"
|
||||
🤖 ✓ Switched to work-notes project
|
||||
|
||||
Project Summary:
|
||||
• 47 entities
|
||||
• 125 observations
|
||||
• 23 relations
|
||||
|
||||
💬 "What did I work on yesterday?"
|
||||
🤖 [Shows recent activity from work-notes project]
|
||||
```
|
||||
|
||||
**Key Capabilities:**
|
||||
- **Instant Project Switching**: Change project context mid-conversation without restart
|
||||
- **Project-Specific Operations**: Operations work within the currently active project context
|
||||
- **Project Discovery**: List all available projects with status indicators
|
||||
- **Session Context**: Maintains active project throughout conversation
|
||||
- **Backward Compatibility**: Existing single-project setups continue to work seamlessly
|
||||
|
||||
### 2. Advanced Note Editing ✏️
|
||||
|
||||
**Edit notes incrementally without rewriting entire documents:**
|
||||
|
||||
```python
|
||||
# Append new sections to existing notes
|
||||
edit_note("project-planning", "append", "\n## New Requirements\n- Feature X\n- Feature Y")
|
||||
|
||||
# Prepend timestamps to meeting notes
|
||||
edit_note("meeting-notes", "prepend", "## 2025-05-27 Update\n- Progress update...")
|
||||
|
||||
# Replace specific sections under headers
|
||||
edit_note("api-spec", "replace_section", "New implementation details", section="## Implementation")
|
||||
|
||||
# Find and replace with validation
|
||||
edit_note("config", "find_replace", "v0.13.0", find_text="v0.12.0", expected_replacements=2)
|
||||
```
|
||||
|
||||
**Key Capabilities:**
|
||||
- **Append Operations**: Add content to end of notes (most common use case)
|
||||
- **Prepend Operations**: Add content to beginning of notes
|
||||
- **Section Replacement**: Replace content under specific markdown headers
|
||||
- **Find & Replace**: Simple text replacements with occurrence counting
|
||||
- **Smart Error Handling**: Helpful guidance when operations fail
|
||||
- **Project Context**: Works within the active project with session awareness
|
||||
|
||||
### 3. Smart File Management 📁
|
||||
|
||||
**Move and organize notes:**
|
||||
|
||||
```python
|
||||
# Simple moves with automatic folder creation
|
||||
move_note("my-note", "work/projects/my-note.md")
|
||||
|
||||
# Organize within the active project
|
||||
move_note("shared-doc", "archive/old-docs/shared-doc.md")
|
||||
|
||||
# Rename operations
|
||||
move_note("old-name", "same-folder/new-name.md")
|
||||
```
|
||||
|
||||
**Key Capabilities:**
|
||||
- **Database Consistency**: Updates file paths, permalinks, and checksums automatically
|
||||
- **Search Reindexing**: Maintains search functionality after moves
|
||||
- **Folder Creation**: Automatically creates destination directories
|
||||
- **Project Isolation**: Operates within the currently active project
|
||||
- **Link Preservation**: Maintains internal links and references
|
||||
|
||||
### 4. Enhanced Search & Discovery 🔍
|
||||
|
||||
**Find content more easily with improved search capabilities:**
|
||||
|
||||
- **Frontmatter Tag Search**: Tags from YAML frontmatter are now indexed and searchable
|
||||
- **Improved Content Discovery**: Search across titles, content, tags, and metadata
|
||||
- **Project-Scoped Search**: Search within the currently active project
|
||||
- **Better Search Quality**: Enhanced FTS5 indexing with tag content inclusion
|
||||
|
||||
**Example:**
|
||||
```yaml
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
tags: [coffee, brewing, equipment]
|
||||
---
|
||||
```
|
||||
Now searchable by: "coffee", "brewing", "equipment", or "Coffee Brewing Methods"
|
||||
|
||||
### 5. Unified Database Architecture 🗄️
|
||||
|
||||
**Single app-level database for better performance and project management:**
|
||||
|
||||
- **Migration from Per-Project DBs**: Moved from multiple SQLite files to single app database
|
||||
- **Project Isolation**: Proper data separation with project_id foreign keys
|
||||
- **Better Performance**: Optimized queries and reduced file I/O
|
||||
|
||||
## Complete MCP Tool Suite 🛠️
|
||||
|
||||
### New Project Management Tools
|
||||
- **`list_projects()`** - Discover and list all available projects with status
|
||||
- **`switch_project(project_name)`** - Change active project context during conversations
|
||||
- **`get_current_project()`** - Show currently active project with statistics
|
||||
- **`set_default_project(project_name)`** - Update default project configuration
|
||||
- **`sync_status()`** - Check file synchronization status and background operations
|
||||
|
||||
### New Note Operations Tools
|
||||
- **`edit_note()`** - Incremental note editing (append, prepend, find/replace, section replace)
|
||||
- **`move_note()`** - Move notes with database consistency and search reindexing
|
||||
- **`view_note()`** - Display notes as formatted artifacts for better readability in Claude Desktop
|
||||
|
||||
### Enhanced Existing Tools
|
||||
All existing tools now support:
|
||||
- **Session context awareness** (operates within the currently active project)
|
||||
- **Enhanced error messages** with project context metadata
|
||||
- **Improved response formatting** with project information footers
|
||||
- **Project isolation** ensures operations stay within the correct project boundaries
|
||||
|
||||
|
||||
## User Experience Improvements
|
||||
|
||||
### Installation Options
|
||||
|
||||
**Multiple ways to install and test Basic Memory:**
|
||||
|
||||
```bash
|
||||
# Stable release
|
||||
uv tool install basic-memory
|
||||
|
||||
# Beta/pre-releases
|
||||
uv tool install basic-memory --pre
|
||||
```
|
||||
|
||||
|
||||
### Bug Fixes & Quality Improvements
|
||||
|
||||
**Major issues resolved in v0.13.0:**
|
||||
|
||||
- **#118**: Fixed YAML tag formatting to follow standard specification
|
||||
- **#110**: Fixed `--project` flag consistency across all CLI commands
|
||||
- **#107**: Fixed write_note update failures with existing notes
|
||||
- **#93**: Fixed custom permalink handling in frontmatter
|
||||
- **#52**: Enhanced search capabilities with frontmatter tag indexing
|
||||
- **FTS5 Search**: Fixed special character handling in search queries
|
||||
- **Error Handling**: Improved error messages and validation across all tools
|
||||
|
||||
## Breaking Changes & Migration
|
||||
|
||||
### For Existing Users
|
||||
|
||||
**Automatic Migration**: First run will automatically migrate existing data to the new unified database structure. No manual action required.
|
||||
|
||||
**What Changes:**
|
||||
- Database location: Moved to `~/.basic-memory/memory.db` (unified across projects)
|
||||
- Configuration: Projects defined in `~/.basic-memory/config.json` are synced with database
|
||||
|
||||
**What Stays the Same:**
|
||||
- All existing notes and data remain unchanged
|
||||
- Default project behavior maintained for single-project users
|
||||
- All existing MCP tools continue to work without modification
|
||||
|
||||
|
||||
|
||||
|
||||
## Documentation & Resources
|
||||
|
||||
### New Documentation
|
||||
- [Project Management Guide](docs/Project%20Management.md) - Multi-project workflows
|
||||
- [Note Editing Guide](docs/Note%20Editing.md) - Advanced editing techniques
|
||||
|
||||
### Updated Documentation
|
||||
- [README.md](README.md) - Installation options and beta build instructions
|
||||
- [CONTRIBUTING.md](CONTRIBUTING.md) - Release process and version management
|
||||
- [CLAUDE.md](CLAUDE.md) - Development workflow and CI/CD documentation
|
||||
- [Claude.ai Integration](docs/Claude.ai%20Integration.md) - Updated MCP tool examples
|
||||
|
||||
### Quick Start Examples
|
||||
|
||||
**Project Switching:**
|
||||
```
|
||||
💬 "Switch to my work project and show recent activity"
|
||||
🤖 [Calls switch_project("work") then recent_activity()]
|
||||
```
|
||||
|
||||
**Note Editing:**
|
||||
```
|
||||
💬 "Add a section about deployment to my API docs"
|
||||
🤖 [Calls edit_note("api-docs", "append", "## Deployment\n...")]
|
||||
```
|
||||
|
||||
**File Organization:**
|
||||
```
|
||||
💬 "Move my old meeting notes to the archive folder"
|
||||
🤖 [Calls move_note("meeting-notes", "archive/old-meetings.md")]
|
||||
```
|
||||
|
||||
|
||||
### Getting Updates
|
||||
```bash
|
||||
# Stable releases
|
||||
uv tool upgrade basic-memory
|
||||
|
||||
# Beta releases
|
||||
uv tool install basic-memory --pre --force-reinstall
|
||||
|
||||
# Latest development
|
||||
uv tool install basic-memory --pre --force-reinstall
|
||||
```
|
||||
@@ -0,0 +1,42 @@
|
||||
# Docker Compose configuration for Basic Memory with PostgreSQL
|
||||
# Use this for local development and testing with Postgres backend
|
||||
#
|
||||
# Usage:
|
||||
# docker-compose -f docker-compose-postgres.yml up -d
|
||||
# docker-compose -f docker-compose-postgres.yml down
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17
|
||||
container_name: basic-memory-postgres
|
||||
environment:
|
||||
# Local development/test credentials - NOT for production
|
||||
# These values are referenced by tests and justfile commands
|
||||
POSTGRES_DB: basic_memory
|
||||
POSTGRES_USER: basic_memory_user
|
||||
POSTGRES_PASSWORD: dev_password # Simple password for local testing only
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U basic_memory_user -d basic_memory"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
# Named volume for Postgres data
|
||||
postgres_data:
|
||||
driver: local
|
||||
|
||||
# Named volume for persistent configuration
|
||||
# Database will be stored in Postgres, not in this volume
|
||||
basic-memory-config:
|
||||
driver: local
|
||||
|
||||
# Network configuration (optional)
|
||||
# networks:
|
||||
# basic-memory-net:
|
||||
# driver: bridge
|
||||
@@ -0,0 +1,83 @@
|
||||
# Docker Compose configuration for Basic Memory
|
||||
# See docs/Docker.md for detailed setup instructions
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
basic-memory:
|
||||
# Use pre-built image (recommended for most users)
|
||||
image: ghcr.io/basicmachines-co/basic-memory:latest
|
||||
|
||||
# Uncomment to build locally instead:
|
||||
# build: .
|
||||
|
||||
container_name: basic-memory-server
|
||||
|
||||
# Volume mounts for knowledge directories and persistent data
|
||||
volumes:
|
||||
|
||||
# Persistent storage for configuration and database
|
||||
- basic-memory-config:/root/.basic-memory:rw
|
||||
|
||||
# Mount your knowledge directory (required)
|
||||
# Change './knowledge' to your actual Obsidian vault or knowledge directory
|
||||
- ./knowledge:/app/data:rw
|
||||
|
||||
# OPTIONAL: Mount additional knowledge directories for multiple projects
|
||||
# - ./work-notes:/app/data/work:rw
|
||||
# - ./personal-notes:/app/data/personal:rw
|
||||
|
||||
# You can edit the project config manually in the mounted config volume
|
||||
# The default project will be configured to use /app/data
|
||||
environment:
|
||||
# Project configuration
|
||||
- BASIC_MEMORY_DEFAULT_PROJECT=main
|
||||
|
||||
# Enable real-time file synchronization (recommended for Docker)
|
||||
- BASIC_MEMORY_SYNC_CHANGES=true
|
||||
|
||||
# Logging configuration
|
||||
- BASIC_MEMORY_LOG_LEVEL=INFO
|
||||
|
||||
# Sync delay in milliseconds (adjust for performance vs responsiveness)
|
||||
- BASIC_MEMORY_SYNC_DELAY=1000
|
||||
|
||||
# Port exposure for HTTP transport (only needed if not using STDIO)
|
||||
ports:
|
||||
- "8000:8000"
|
||||
|
||||
# Command with SSE transport (configurable via environment variables above)
|
||||
# IMPORTANT: The SSE and streamable-http endpoints are not secured
|
||||
command: ["basic-memory", "mcp", "--transport", "sse", "--host", "0.0.0.0", "--port", "8000"]
|
||||
|
||||
# Container management
|
||||
restart: unless-stopped
|
||||
|
||||
# Health monitoring
|
||||
healthcheck:
|
||||
test: ["CMD", "basic-memory", "--version"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
# Optional: Resource limits
|
||||
# deploy:
|
||||
# resources:
|
||||
# limits:
|
||||
# memory: 512M
|
||||
# cpus: '0.5'
|
||||
# reservations:
|
||||
# memory: 256M
|
||||
# cpus: '0.25'
|
||||
|
||||
volumes:
|
||||
# Named volume for persistent configuration and database
|
||||
# This ensures your configuration and knowledge graph persist across container restarts
|
||||
basic-memory-config:
|
||||
driver: local
|
||||
|
||||
# Network configuration (optional)
|
||||
# networks:
|
||||
# basic-memory-net:
|
||||
# driver: bridge
|
||||
@@ -1,431 +0,0 @@
|
||||
---
|
||||
title: AI Assistant Guide
|
||||
type: note
|
||||
permalink: docs/ai-assistant-guide
|
||||
---
|
||||
> Note: This is an optional document that can be copy/pasted into the project knowledge for an LLM to provide a full description of how it can work with Basic Memory. It is provided as a helpful resource. The tools contain extensive usage description prompts with enable the LLM to understand them.
|
||||
|
||||
You can [download](https://github.com/basicmachines-co/basic-memory/blob/main/docs/AI%20Assistant%20Guide.md) the contents of this file from GitHub
|
||||
# AI Assistant Guide for Basic Memory
|
||||
|
||||
This guide helps you, the AI assistant, use Basic Memory tools effectively when working with users. It covers reading, writing, and navigating knowledge through the Model Context Protocol (MCP).
|
||||
|
||||
## Quick Reference
|
||||
|
||||
**Essential Tools:**
|
||||
- `write_note()` - Create/update notes (primary tool)
|
||||
- `read_note()` - Read existing content
|
||||
- `search_notes()` - Find information
|
||||
- `edit_note()` - Modify existing notes incrementally (v0.13.0)
|
||||
- `move_note()` - Organize files with database consistency (v0.13.0)
|
||||
|
||||
**Project Management (v0.13.0):**
|
||||
- `list_projects()` - Show available projects
|
||||
- `switch_project()` - Change active project
|
||||
- `get_current_project()` - Current project info
|
||||
|
||||
**Key Principles:**
|
||||
1. **Build connections** - Rich knowledge graphs > isolated notes
|
||||
2. **Ask permission** - "Would you like me to record this?"
|
||||
3. **Use exact titles** - For accurate `[[WikiLinks]]`
|
||||
4. **Leverage v0.13.0** - Edit incrementally, organize proactively, switch projects contextually
|
||||
|
||||
## Overview
|
||||
|
||||
Basic Memory allows you and users to record context in local Markdown files, building a rich knowledge base through natural conversations. The system automatically creates a semantic knowledge graph from simple text patterns.
|
||||
|
||||
- **Local-First**: All data is stored in plain text files on the user's computer
|
||||
- **Real-Time**: Users see content updates immediately
|
||||
- **Bi-Directional**: Both you and users can read and edit notes
|
||||
- **Semantic**: Simple patterns create a structured knowledge graph
|
||||
- **Persistent**: Knowledge persists across sessions and conversations
|
||||
|
||||
## The Importance of the Knowledge Graph
|
||||
|
||||
Basic Memory's value comes from connections between notes, not just the notes themselves. When writing notes, your primary goal should be creating a rich, interconnected knowledge graph.
|
||||
|
||||
When creating content, focus on:
|
||||
|
||||
1. **Increasing Semantic Density**: Add multiple observations and relations to each note
|
||||
2. **Using Accurate References**: Aim to reference existing entities by their exact titles
|
||||
3. **Creating Forward References**: Feel free to reference entities that don't exist yet - Basic Memory will resolve these when they're created later
|
||||
4. **Creating Bidirectional Links**: When appropriate, connect entities from both directions
|
||||
5. **Using Meaningful Categories**: Add semantic context with appropriate observation categories
|
||||
6. **Choosing Precise Relations**: Use specific relation types that convey meaning
|
||||
|
||||
Remember that a knowledge graph with 10 heavily connected notes is more valuable than 20 isolated notes. Your job is to help build these connections.
|
||||
|
||||
## Core Tools Reference
|
||||
|
||||
### Essential Content Management
|
||||
|
||||
**Writing knowledge** (most important tool):
|
||||
```
|
||||
write_note(
|
||||
title="Search Design",
|
||||
content="# Search Design\n...",
|
||||
folder="specs", # Optional
|
||||
tags=["search", "design"], # v0.13.0: now searchable!
|
||||
project="work-notes" # v0.13.0: target specific project
|
||||
)
|
||||
```
|
||||
|
||||
**Reading knowledge:**
|
||||
```
|
||||
read_note("Search Design") # By title
|
||||
read_note("specs/search-design") # By path
|
||||
read_note("memory://specs/search") # By memory URL
|
||||
```
|
||||
|
||||
**Viewing notes as formatted artifacts (Claude Desktop):**
|
||||
```
|
||||
view_note("Search Design") # Creates readable artifact
|
||||
view_note("specs/search-design") # By permalink
|
||||
view_note("memory://specs/search") # By memory URL
|
||||
```
|
||||
|
||||
**Incremental editing** (v0.13.0):
|
||||
```
|
||||
edit_note(
|
||||
identifier="Search Design", # Must be EXACT title/permalink (strict matching)
|
||||
operation="append", # append, prepend, find_replace, replace_section
|
||||
content="\n## New Section\nContent here..."
|
||||
)
|
||||
```
|
||||
**⚠️ Important:** `edit_note` requires exact identifiers (no fuzzy matching). Use `search_notes()` first if uncertain.
|
||||
|
||||
**File organization** (v0.13.0):
|
||||
```
|
||||
move_note(
|
||||
identifier="Old Note", # Must be EXACT title/permalink (strict matching)
|
||||
destination="archive/old-note.md" # Folders created automatically
|
||||
)
|
||||
```
|
||||
**⚠️ Important:** `move_note` requires exact identifiers (no fuzzy matching). Use `search_notes()` first if uncertain.
|
||||
|
||||
### Project Management (v0.13.0)
|
||||
|
||||
```
|
||||
list_projects() # Show available projects
|
||||
switch_project("work-notes") # Change active project
|
||||
get_current_project() # Current project info
|
||||
```
|
||||
|
||||
### Search & Discovery
|
||||
|
||||
```
|
||||
search_notes("authentication system") # v0.13.0: includes frontmatter tags
|
||||
build_context("memory://specs/search") # Follow knowledge graph connections
|
||||
recent_activity(timeframe="1 week") # Check what's been updated
|
||||
```
|
||||
|
||||
## memory:// URLs Explained
|
||||
|
||||
Basic Memory uses a special URL format to reference entities in the knowledge graph:
|
||||
|
||||
- `memory://title` - Reference by title
|
||||
- `memory://folder/title` - Reference by folder and title
|
||||
- `memory://permalink` - Reference by permalink
|
||||
- `memory://path/relation_type/*` - Follow all relations of a specific type
|
||||
- `memory://path/*/target` - Find all entities with relations to target
|
||||
|
||||
## Semantic Markdown Format
|
||||
|
||||
Knowledge is encoded in standard markdown using simple patterns:
|
||||
|
||||
**Observations** - Facts about an entity:
|
||||
```markdown
|
||||
- [category] This is an observation #tag1 #tag2 (optional context)
|
||||
```
|
||||
|
||||
**Relations** - Links between entities:
|
||||
```markdown
|
||||
- relation_type [[Target Entity]] (optional context)
|
||||
```
|
||||
|
||||
**Common Categories & Relation Types:**
|
||||
- Categories: `[idea]`, `[decision]`, `[question]`, `[fact]`, `[requirement]`, `[technique]`, `[recipe]`, `[preference]`
|
||||
- Relations: `relates_to`, `implements`, `requires`, `extends`, `part_of`, `pairs_with`, `inspired_by`, `originated_from`
|
||||
|
||||
## When to Record Context
|
||||
|
||||
**Always consider recording context when**:
|
||||
|
||||
1. Users make decisions or reach conclusions
|
||||
2. Important information emerges during conversation
|
||||
3. Multiple related topics are discussed
|
||||
4. The conversation contains information that might be useful later
|
||||
5. Plans, tasks, or action items are mentioned
|
||||
|
||||
**Protocol for recording context**:
|
||||
|
||||
1. Identify valuable information in the conversation
|
||||
2. Ask the user: "Would you like me to record our discussion about [topic] in Basic Memory?"
|
||||
3. If they agree, use `write_note` to capture the information
|
||||
4. If they decline, continue without recording
|
||||
5. Let the user know when information has been recorded: "I've saved our discussion about [topic] to Basic Memory."
|
||||
|
||||
## Understanding User Interactions
|
||||
|
||||
Users will interact with Basic Memory in patterns like:
|
||||
|
||||
1. **Creating knowledge**:
|
||||
```
|
||||
Human: "Let's write up what we discussed about search."
|
||||
|
||||
You: I'll create a note capturing our discussion about the search functionality.
|
||||
[Use write_note() to record the conversation details]
|
||||
```
|
||||
|
||||
2. **Referencing existing knowledge**:
|
||||
```
|
||||
Human: "Take a look at memory://specs/search"
|
||||
|
||||
You: I'll examine that information.
|
||||
[Use build_context() to gather related information]
|
||||
[Then read_note() to access specific content]
|
||||
```
|
||||
|
||||
3. **Finding information**:
|
||||
```
|
||||
Human: "What were our decisions about auth?"
|
||||
|
||||
You: Let me find that information for you.
|
||||
[Use search_notes() to find relevant notes]
|
||||
[Then build_context() to understand connections]
|
||||
```
|
||||
|
||||
4. **Editing existing notes (v0.13.0)**:
|
||||
```
|
||||
Human: "Add a section about deployment to my API documentation"
|
||||
|
||||
You: I'll add that section to your existing documentation.
|
||||
[Use edit_note() with operation="append" to add new content]
|
||||
```
|
||||
|
||||
5. **Project management (v0.13.0)**:
|
||||
```
|
||||
Human: "Switch to my work project and show recent activity"
|
||||
|
||||
You: I'll switch to your work project and check what's been updated recently.
|
||||
[Use switch_project() then recent_activity()]
|
||||
```
|
||||
|
||||
6. **File organization (v0.13.0)**:
|
||||
```
|
||||
Human: "Move my old meeting notes to the archive folder"
|
||||
|
||||
You: I'll organize those notes for you.
|
||||
[Use move_note() to relocate files with database consistency]
|
||||
```
|
||||
|
||||
## Key Things to Remember
|
||||
|
||||
1. **Files are Truth**
|
||||
- All knowledge lives in local files on the user's computer
|
||||
- Users can edit files outside your interaction
|
||||
- Changes need to be synced by the user (usually automatic)
|
||||
- Always verify information is current with `recent_activity()`
|
||||
|
||||
2. **Building Context Effectively**
|
||||
- Start with specific entities
|
||||
- Follow meaningful relations
|
||||
- Check recent changes
|
||||
- Build context incrementally
|
||||
- Combine related information
|
||||
|
||||
3. **Writing Knowledge Wisely**
|
||||
- Same title+folder overwrites existing notes
|
||||
- Structure with clear headings and semantic markup
|
||||
- Use tags for searchability (v0.13.0: frontmatter tags indexed)
|
||||
- Keep files organized in logical folders
|
||||
|
||||
4. **Leverage v0.13.0 Features**
|
||||
- **Edit incrementally**: Use `edit_note()` for small changes vs rewriting
|
||||
- **Switch projects**: Change context when user mentions different work areas
|
||||
- **Organize proactively**: Move old content to archive folders
|
||||
- **Cross-project operations**: Create notes in specific projects while maintaining context
|
||||
|
||||
## Common Knowledge Patterns
|
||||
|
||||
### Capturing Decisions
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
tags: [coffee, brewing, pour-over, techniques] # v0.13.0: Now searchable!
|
||||
---
|
||||
|
||||
# Coffee Brewing Methods
|
||||
|
||||
## Context
|
||||
I've experimented with various brewing methods including French press, pour over, and espresso.
|
||||
|
||||
## Decision
|
||||
Pour over is my preferred method for light to medium roasts because it highlights subtle flavors and offers more control over the extraction.
|
||||
|
||||
## Observations
|
||||
- [technique] Blooming the coffee grounds for 30 seconds improves extraction #brewing
|
||||
- [preference] Water temperature between 195-205°F works best #temperature
|
||||
- [equipment] Gooseneck kettle provides better control of water flow #tools
|
||||
- [timing] Total brew time of 3-4 minutes produces optimal extraction #process
|
||||
|
||||
## Relations
|
||||
- pairs_with [[Light Roast Beans]]
|
||||
- contrasts_with [[French Press Method]]
|
||||
- requires [[Proper Grinding Technique]]
|
||||
- part_of [[Morning Coffee Routine]]
|
||||
```
|
||||
|
||||
### Recording Project Structure
|
||||
|
||||
```markdown
|
||||
# Garden Planning
|
||||
|
||||
## Overview
|
||||
This document outlines the garden layout and planting strategy for this season.
|
||||
|
||||
## Observations
|
||||
- [structure] Raised beds in south corner for sun exposure #layout
|
||||
- [structure] Drip irrigation system installed for efficiency #watering
|
||||
- [pattern] Companion planting used to deter pests naturally #technique
|
||||
|
||||
## Relations
|
||||
- contains [[Vegetable Section]]
|
||||
- contains [[Herb Garden]]
|
||||
- implements [[Organic Gardening Principles]]
|
||||
```
|
||||
|
||||
### Technical Discussions
|
||||
|
||||
```markdown
|
||||
# Recipe Improvement Discussion
|
||||
|
||||
## Key Points
|
||||
Discussed strategies for improving the chocolate chip cookie recipe.
|
||||
|
||||
## Observations
|
||||
- [issue] Cookies spread too thin when baked at 350°F #texture
|
||||
- [solution] Chilling dough for 24 hours improves flavor and reduces spreading #technique
|
||||
- [decision] Will use brown butter instead of regular butter #flavor
|
||||
|
||||
## Relations
|
||||
- improves [[Basic Cookie Recipe]]
|
||||
- inspired_by [[Bakery-Style Cookies]]
|
||||
- pairs_with [[Homemade Ice Cream]]
|
||||
```
|
||||
|
||||
## v0.13.0 Workflow Examples
|
||||
|
||||
### Multi-Project Conversations
|
||||
|
||||
**User:** "I need to update my work documentation and also add a personal recipe note."
|
||||
|
||||
**Workflow:**
|
||||
1. `list_projects()` - Check available projects
|
||||
2. `write_note(title="Sprint Planning", project="work-notes")` - Work content
|
||||
3. `write_note(title="Weekend Recipes", project="personal")` - Personal content
|
||||
|
||||
### Incremental Note Building
|
||||
|
||||
**User:** "Add a troubleshooting section to my setup guide."
|
||||
|
||||
**Workflow:**
|
||||
1. `edit_note(identifier="Setup Guide", operation="append", content="\n## Troubleshooting\n...")`
|
||||
|
||||
**User:** "Update the authentication section in my API docs."
|
||||
|
||||
**Workflow:**
|
||||
1. `edit_note(identifier="API Documentation", operation="replace_section", section="## Authentication")`
|
||||
|
||||
### Smart File Organization
|
||||
|
||||
**User:** "My notes are getting messy in the main folder."
|
||||
|
||||
**Workflow:**
|
||||
1. `move_note("Old Meeting Notes", "archive/2024/old-meetings.md")`
|
||||
2. `move_note("Project Notes", "projects/client-work/notes.md")`
|
||||
|
||||
### Creating Effective Relations
|
||||
|
||||
When creating relations:
|
||||
1. **Reference existing entities** by their exact title: `[[Exact Title]]`
|
||||
2. **Create forward references** to entities that don't exist yet - they'll be linked automatically when created
|
||||
3. **Search first** to find existing entities to reference
|
||||
4. **Use meaningful relation types**: `implements`, `requires`, `part_of` vs generic `relates_to`
|
||||
|
||||
**Example workflow:**
|
||||
1. `search_notes("travel")` to find existing travel-related notes
|
||||
2. Reference found entities: `- part_of [[Japan Travel Guide]]`
|
||||
3. Add forward references: `- located_in [[Tokyo]]` (even if Tokyo note doesn't exist yet)
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
**Missing Content:**
|
||||
- Try `search_notes()` with broader terms if `read_note()` fails
|
||||
- Use fuzzy matching: search for partial titles
|
||||
|
||||
**Forward References:**
|
||||
- These are normal! Basic Memory links them automatically when target notes are created
|
||||
- Inform users: "I've created forward references that will be linked when you create those notes"
|
||||
|
||||
**Sync Issues:**
|
||||
- If information seems outdated, suggest `basic-memory sync`
|
||||
- Use `recent_activity()` to check if content is current
|
||||
|
||||
**Strict Mode for Edit/Move Operations:**
|
||||
- `edit_note()` and `move_note()` require **exact identifiers** (no fuzzy matching for safety)
|
||||
- If identifier not found: use `search_notes()` first to find the exact title/permalink
|
||||
- Error messages will guide you to find correct identifiers
|
||||
- Example workflow:
|
||||
```
|
||||
# ❌ This might fail if identifier isn't exact
|
||||
edit_note("Meeting Note", "append", "content")
|
||||
|
||||
# ✅ Safe approach: search first, then use exact result
|
||||
results = search_notes("meeting")
|
||||
edit_note("Meeting Notes 2024", "append", "content") # Use exact title from search
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Proactively Record Context**
|
||||
- Offer to capture important discussions
|
||||
- Record decisions, rationales, and conclusions
|
||||
- Link to related topics
|
||||
- Ask for permission first: "Would you like me to save our discussion about [topic]?"
|
||||
- Confirm when complete: "I've saved our discussion to Basic Memory"
|
||||
|
||||
2. **Create a Rich Semantic Graph**
|
||||
- **Add meaningful observations**: Include at least 3-5 categorized observations in each note
|
||||
- **Create deliberate relations**: Connect each note to at least 2-3 related entities
|
||||
- **Use existing entities**: Before creating a new relation, search for existing entities
|
||||
- **Verify wikilinks**: When referencing `[[Entity]]`, use exact titles of existing notes
|
||||
- **Check accuracy**: Use `search_notes()` or `recent_activity()` to confirm entity titles
|
||||
- **Use precise relation types**: Choose specific relation types that convey meaning (e.g., "implements" instead of "relates_to")
|
||||
- **Consider bidirectional relations**: When appropriate, create inverse relations in both entities
|
||||
|
||||
3. **Structure Content Thoughtfully**
|
||||
- Use clear, descriptive titles
|
||||
- Organize with logical sections (Context, Decision, Implementation, etc.)
|
||||
- Include relevant context and background
|
||||
- Add semantic observations with appropriate categories
|
||||
- Use a consistent format for similar types of notes
|
||||
- Balance detail with conciseness
|
||||
|
||||
4. **Navigate Knowledge Effectively**
|
||||
- Start with specific searches
|
||||
- Follow relation paths
|
||||
- Combine information from multiple sources
|
||||
- Verify information is current
|
||||
- Build a complete picture before responding
|
||||
|
||||
5. **Help Users Maintain Their Knowledge**
|
||||
- Suggest organizing related topics
|
||||
- Identify potential duplicates
|
||||
- Recommend adding relations between topics
|
||||
- Offer to create summaries of scattered information
|
||||
- Suggest potential missing relations: "I notice this might relate to [topic], would you like me to add that connection?"
|
||||
|
||||
|
||||
Built with ♥️ by Basic Machines
|
||||
@@ -1,414 +0,0 @@
|
||||
---
|
||||
title: CLI Reference
|
||||
type: note
|
||||
permalink: docs/cli-reference
|
||||
---
|
||||
|
||||
# CLI Reference
|
||||
|
||||
Basic Memory provides command line tools for managing your knowledge base. This reference covers the available commands and their options.
|
||||
|
||||
## Core Commands
|
||||
|
||||
### auth (New in v0.13.0)
|
||||
|
||||
Manage OAuth authentication for secure remote access:
|
||||
|
||||
```bash
|
||||
# Test authentication setup
|
||||
basic-memory auth test-auth
|
||||
|
||||
# Register OAuth client
|
||||
basic-memory auth register-client
|
||||
```
|
||||
|
||||
Supports multiple authentication providers:
|
||||
- **Basic Provider**: For development and testing
|
||||
- **Supabase Provider**: For production deployments
|
||||
- **External Providers**: GitHub, Google integration framework
|
||||
|
||||
See [[OAuth Authentication Guide]] for complete setup instructions.
|
||||
|
||||
### sync
|
||||
|
||||
Keeps files and the knowledge graph in sync:
|
||||
|
||||
```bash
|
||||
# Basic sync
|
||||
basic-memory sync
|
||||
|
||||
# Watch for changes
|
||||
basic-memory sync --watch
|
||||
|
||||
# Show detailed sync information
|
||||
basic-memory sync --verbose
|
||||
```
|
||||
|
||||
Options:
|
||||
- `--watch`: Continuously monitor for changes
|
||||
- `--verbose`: Show detailed output
|
||||
|
||||
**Note**:
|
||||
|
||||
As of the v0.12.0 release syncing will occur in real time when the mcp process starts.
|
||||
- The real time sync means that it is no longer necessary to run the `basic-memory sync --watch` process in a a terminal to sync changes to the db (so the AI can see them). This will be done automatically.
|
||||
|
||||
This behavior can be changed via the config. The config file for Basic Memory is in the home directory under `.basic-memory/config.json`.
|
||||
|
||||
To change the properties, set the following values:
|
||||
```
|
||||
~/.basic-memory/config.json
|
||||
{
|
||||
"sync_changes": false,
|
||||
}
|
||||
```
|
||||
|
||||
Thanks for using Basic Memory!
|
||||
### import (Enhanced in v0.13.0)
|
||||
|
||||
Imports external knowledge sources with support for project targeting:
|
||||
|
||||
```bash
|
||||
# Claude conversations
|
||||
basic-memory import claude conversations
|
||||
|
||||
# Claude projects
|
||||
basic-memory import claude projects
|
||||
|
||||
# ChatGPT history
|
||||
basic-memory import chatgpt
|
||||
|
||||
# Memory JSON format
|
||||
basic-memory import memory-json /path/to/memory.json
|
||||
|
||||
# Import to specific project (v0.13.0)
|
||||
basic-memory --project=work import claude conversations
|
||||
```
|
||||
|
||||
**New in v0.13.0:**
|
||||
- **Project Targeting**: Import directly to specific projects
|
||||
- **Real-time Sync**: Imported content available immediately
|
||||
- **Unified Database**: All imports stored in centralized database
|
||||
|
||||
> **Note**: Changes sync automatically - no manual sync required in v0.13.0.
|
||||
### status
|
||||
|
||||
Shows system status information:
|
||||
|
||||
```bash
|
||||
# Basic status check
|
||||
basic-memory status
|
||||
|
||||
# Detailed status
|
||||
basic-memory status --verbose
|
||||
|
||||
# JSON output
|
||||
basic-memory status --json
|
||||
```
|
||||
|
||||
|
||||
### project (Enhanced in v0.13.0)
|
||||
|
||||
Manage multiple projects with the new unified database architecture. Projects can now be switched instantly during conversations without restart.
|
||||
|
||||
```bash
|
||||
# List all configured projects with status
|
||||
basic-memory project list
|
||||
|
||||
# Create a new project
|
||||
basic-memory project create work ~/work-basic-memory
|
||||
|
||||
# Set the default project
|
||||
basic-memory project set-default work
|
||||
|
||||
# Delete a project (doesn't delete files)
|
||||
basic-memory project delete personal
|
||||
|
||||
# Show detailed project statistics
|
||||
basic-memory project info
|
||||
```
|
||||
|
||||
**New in v0.13.0:**
|
||||
- **Unified Database**: All projects share a single database for better performance
|
||||
- **Instant Switching**: Switch projects during conversations without restart
|
||||
- **Enhanced Commands**: Updated project commands with better status information
|
||||
- **Project Statistics**: Detailed info about entities, observations, and relations
|
||||
|
||||
#### Using Projects in Commands
|
||||
|
||||
All commands support the `--project` flag to specify which project to use:
|
||||
|
||||
```bash
|
||||
# Sync a specific project
|
||||
basic-memory --project=work sync
|
||||
|
||||
# Run MCP server for a specific project
|
||||
basic-memory --project=personal mcp
|
||||
```
|
||||
|
||||
You can also set the `BASIC_MEMORY_PROJECT` environment variable:
|
||||
|
||||
```bash
|
||||
BASIC_MEMORY_PROJECT=work basic-memory sync
|
||||
```
|
||||
|
||||
### tool (Enhanced in v0.13.0)
|
||||
|
||||
Direct access to MCP tools via CLI with new editing and file management capabilities:
|
||||
|
||||
```bash
|
||||
# Create notes
|
||||
basic-memory tool write-note --title "My Note" --content "Content here"
|
||||
|
||||
# Edit notes incrementally (v0.13.0)
|
||||
echo "New content" | basic-memory tool edit-note --title "My Note" --operation append
|
||||
|
||||
# Move notes (v0.13.0)
|
||||
basic-memory tool move-note --identifier "My Note" --destination "archive/my-note.md"
|
||||
|
||||
# Search notes
|
||||
basic-memory tool search-notes --query "authentication"
|
||||
|
||||
# Project management (v0.13.0)
|
||||
basic-memory tool list-projects
|
||||
basic-memory tool switch-project --project-name "work"
|
||||
```
|
||||
|
||||
**New in v0.13.0:**
|
||||
- **edit-note**: Incremental editing (append, prepend, find/replace, section replace)
|
||||
- **move-note**: File management with database consistency
|
||||
- **Project tools**: list-projects, switch-project, get-current-project
|
||||
- **Cross-project operations**: Use `--project` flag with any tool
|
||||
|
||||
### help
|
||||
|
||||
The full list of commands and help for each can be viewed with the `--help` argument.
|
||||
|
||||
```
|
||||
✗ basic-memory --help
|
||||
|
||||
Usage: basic-memory [OPTIONS] COMMAND [ARGS]...
|
||||
|
||||
Basic Memory - Local-first personal knowledge management system.
|
||||
|
||||
╭─ Options ─────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ --project -p TEXT Specify which project to use │
|
||||
│ [env var: BASIC_MEMORY_PROJECT] │
|
||||
│ [default: None] │
|
||||
│ --version -V Show version information and exit. │
|
||||
│ --install-completion Install completion for the current shell. │
|
||||
│ --show-completion Show completion for the current shell, to copy it or │
|
||||
│ customize the installation. │
|
||||
│ --help Show this message and exit. │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭─ Commands ────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ auth OAuth authentication management (v0.13.0) │
|
||||
│ sync Sync knowledge files with the database │
|
||||
│ status Show sync status between files and database │
|
||||
│ reset Reset database (drop all tables and recreate) │
|
||||
│ mcp Run the MCP server for Claude Desktop integration │
|
||||
│ import Import data from various sources │
|
||||
│ tool Direct access to MCP tools via CLI │
|
||||
│ project Manage multiple Basic Memory projects │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
## Initial Setup
|
||||
|
||||
```bash
|
||||
# Install Basic Memory
|
||||
uv install basic-memory
|
||||
|
||||
# First sync
|
||||
basic-memory sync
|
||||
|
||||
# Start watching mode
|
||||
basic-memory sync --watch
|
||||
```
|
||||
|
||||
> **Important**: You need to install Basic Memory via `uv` or `pip` to use the command line tools, see [[Getting Started with Basic Memory#Installation]].
|
||||
|
||||
## Regular Usage
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
basic-memory status
|
||||
|
||||
# Import new content
|
||||
basic-memory import claude conversations
|
||||
|
||||
# Sync changes
|
||||
basic-memory sync
|
||||
|
||||
# Sync changes continuously
|
||||
basic-memory sync --watch
|
||||
```
|
||||
|
||||
## Maintenance Tasks
|
||||
|
||||
```bash
|
||||
# Check system status in detail
|
||||
basic-memory status --verbose
|
||||
|
||||
# Full resync of all files
|
||||
basic-memory sync
|
||||
|
||||
# Import updates to specific folder
|
||||
basic-memory import claude conversations --folder new
|
||||
```
|
||||
|
||||
|
||||
## Using stdin with Basic Memory's `write_note` Tool
|
||||
|
||||
The `write-note` tool supports reading content from standard input (stdin), allowing for more flexible workflows when creating or updating notes in your Basic Memory knowledge base.
|
||||
|
||||
### Use Cases
|
||||
|
||||
This feature is particularly useful for:
|
||||
|
||||
1. **Piping output from other commands** directly into Basic Memory notes
|
||||
2. **Creating notes with multi-line content** without having to escape quotes or special characters
|
||||
3. **Integrating with AI assistants** like Claude Code that can generate content and pipe it to Basic Memory
|
||||
4. **Processing text data** from files or other sources
|
||||
|
||||
### Basic Usage
|
||||
|
||||
#### Method 1: Using a Pipe
|
||||
|
||||
You can pipe content from another command into `write_note`:
|
||||
|
||||
```bash
|
||||
# Pipe output of a command into a new note
|
||||
echo "# My Note\n\nThis is a test note" | basic-memory tool write-note --title "Test Note" --folder "notes"
|
||||
|
||||
# Pipe output of a file into a new note
|
||||
cat README.md | basic-memory tool write-note --title "Project README" --folder "documentation"
|
||||
|
||||
# Process text through other tools before saving as a note
|
||||
cat data.txt | grep "important" | basic-memory tool write-note --title "Important Data" --folder "data"
|
||||
```
|
||||
|
||||
#### Method 2: Using Heredoc Syntax
|
||||
|
||||
For multi-line content, you can use heredoc syntax:
|
||||
|
||||
```bash
|
||||
# Create a note with heredoc
|
||||
cat << EOF | basic-memory tool write_note --title "Project Ideas" --folder "projects"
|
||||
# Project Ideas for Q2
|
||||
|
||||
## AI Integration
|
||||
- Improve recommendation engine
|
||||
- Add semantic search to product catalog
|
||||
|
||||
## Infrastructure
|
||||
- Migrate to Kubernetes
|
||||
- Implement CI/CD pipeline
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Method 3: Input Redirection
|
||||
|
||||
You can redirect input from a file:
|
||||
|
||||
```bash
|
||||
# Create a note from file content
|
||||
basic-memory tool write-note --title "Meeting Notes" --folder "meetings" < meeting_notes.md
|
||||
```
|
||||
|
||||
## Integration with Claude Code
|
||||
|
||||
This feature works well with Claude Code in the terminal:
|
||||
|
||||
### cli
|
||||
|
||||
In a Claude Code session, let Claude know he can use the basic-memory tools, then he can execute them via the cli:
|
||||
|
||||
```
|
||||
⏺ Bash(echo "# Test Note from Claude\n\nThis is a test note created by Claude to test the stdin functionality." | basic-memory tool write-note --title "Claude Test Note" --folder "test" --tags "test" --tags "claude")…
|
||||
⎿ # Created test/Claude Test Note.md (23e00eec)
|
||||
permalink: test/claude-test-note
|
||||
|
||||
## Tags
|
||||
- test, claude
|
||||
|
||||
```
|
||||
|
||||
### MCP
|
||||
|
||||
Claude code can also now use mcp tools, so it can use any of the basic-memory tool natively. To install basic-memory in Claude Code:
|
||||
|
||||
Run
|
||||
```
|
||||
claude mcp add basic-memory basic-memory mcp
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
➜ ~ claude mcp add basic-memory basic-memory mcp
|
||||
Added stdio MCP server basic-memory with command: basic-memory mcp to project config
|
||||
➜ ~ claude mcp list
|
||||
basic-memory: basic-memory mcp
|
||||
```
|
||||
|
||||
You can then use the `/mcp` command in the REPL:
|
||||
|
||||
```
|
||||
/mcp
|
||||
⎿ MCP Server Status
|
||||
|
||||
• basic-memory: connected
|
||||
```
|
||||
|
||||
## Version Management (New in v0.13.0)
|
||||
|
||||
Basic Memory v0.13.0 introduces automatic version management and multiple installation options:
|
||||
|
||||
```bash
|
||||
# Stable releases
|
||||
pip install basic-memory
|
||||
|
||||
# Beta/pre-releases
|
||||
pip install basic-memory --pre
|
||||
|
||||
# Latest development builds (auto-published)
|
||||
pip install basic-memory --pre --force-reinstall
|
||||
|
||||
# Check current version
|
||||
basic-memory --version
|
||||
```
|
||||
|
||||
**Version Types:**
|
||||
- **Stable**: `0.13.0` (manual git tags)
|
||||
- **Beta**: `0.13.0b1` (manual git tags)
|
||||
- **Development**: `0.12.4.dev26+468a22f` (automatic from commits)
|
||||
|
||||
## Troubleshooting Common Issues
|
||||
|
||||
### Sync Conflicts
|
||||
|
||||
If you encounter a file changed during sync error:
|
||||
1. Check the file referenced in the error message
|
||||
2. Resolve any conflicts manually
|
||||
3. Run sync again
|
||||
|
||||
### Import Errors
|
||||
|
||||
If import fails:
|
||||
1. Check that the source file is in the correct format
|
||||
2. Verify permissions on the target directory
|
||||
3. Use --verbose flag for detailed error information
|
||||
|
||||
### Status Issues
|
||||
|
||||
If status shows problems:
|
||||
1. Note any unresolved relations or warnings
|
||||
2. Run a full sync to attempt automatic resolution
|
||||
3. Check file permissions if database access errors occur
|
||||
|
||||
|
||||
## Relations
|
||||
- used_by [[Getting Started with Basic Memory]] (Installation instructions)
|
||||
- complements [[User Guide]] (How to use Basic Memory)
|
||||
- relates_to [[Introduction to Basic Memory]] (System overview)
|
||||
@@ -1,107 +0,0 @@
|
||||
---
|
||||
title: Canvas Visualizations
|
||||
type: note
|
||||
permalink: docs/canvas
|
||||
tags:
|
||||
- visualization
|
||||
- mapping
|
||||
- obsidian
|
||||
---
|
||||
|
||||
# Canvas Visualizations
|
||||
|
||||
Basic Memory can create visual knowledge maps using Obsidian's Canvas feature. These visualizations help you understand relationships between concepts, map out processes, and visualize your knowledge structure.
|
||||
|
||||
## Creating Canvas Visualizations
|
||||
|
||||
Ask Claude to create a visualization by describing what you want to map:
|
||||
|
||||
```
|
||||
You: "Create a canvas visualization of my project components and their relationships."
|
||||
|
||||
You: "Make a concept map showing the main themes from our discussion about climate change."
|
||||
|
||||
You: "Can you make a canvas diagram of the perfect pour over method?"
|
||||
```
|
||||
|
||||
![[Canvas.png]]
|
||||
|
||||
## Types of Visualizations
|
||||
|
||||
Basic Memory can create several types of visual maps:
|
||||
|
||||
### Document Maps
|
||||
Visualize connections between your notes and documents
|
||||
|
||||
### Concept Maps
|
||||
Create visual representations of ideas and their relationships
|
||||
|
||||
### Process Diagrams
|
||||
Map workflows, sequences, and procedures
|
||||
|
||||
### Thematic Analysis
|
||||
Organize ideas around central themes
|
||||
|
||||
### Relationship Networks
|
||||
Show how different entities relate to each other
|
||||
|
||||
## Visualization Sources
|
||||
|
||||
Claude can create visualizations based on:
|
||||
|
||||
### Documents in Your Knowledge Base
|
||||
```
|
||||
You: "Create a canvas showing the connections between my project planning documents"
|
||||
```
|
||||
|
||||
### Conversation Content
|
||||
```
|
||||
You: "Make a canvas visualization of the main points we just discussed"
|
||||
```
|
||||
|
||||
### Search Results
|
||||
```
|
||||
You: "Find all my notes about psychology and create a visual map of the concepts"
|
||||
```
|
||||
|
||||
### Themes and Relationships
|
||||
```
|
||||
You: "Create a visual map showing how different philosophical schools relate to each other"
|
||||
```
|
||||
|
||||
## Visualization Workflow
|
||||
|
||||
1. **Request a visualization** by describing what you want to see
|
||||
2. **Claude creates the canvas file** in your Basic Memory directory
|
||||
3. **Open the file in Obsidian** to view the visualization
|
||||
4. **Refine the visualization** by asking Claude for adjustments:
|
||||
```
|
||||
You: "Could you reorganize the canvas to group related components together?"
|
||||
|
||||
You: "Please add more detail about the connection between these two concepts."
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
||||
Behind the scenes, Claude:
|
||||
|
||||
1. Creates a `.canvas` file in JSON format
|
||||
2. Adds nodes for each concept or document
|
||||
3. Creates edges to represent relationships
|
||||
4. Sets positions for visual clarity
|
||||
5. Includes any relevant metadata
|
||||
|
||||
The resulting file is fully compatible with Obsidian's Canvas feature and can be edited directly in Obsidian.
|
||||
|
||||
## Tips for Effective Visualizations
|
||||
|
||||
- **Be specific** about what you want to visualize
|
||||
- **Specify the level of detail** you need
|
||||
- **Mention the visualization type** you want (concept map, process flow, etc.)
|
||||
- **Start simple** and ask for refinements
|
||||
- **Provide context** about what documents or concepts to include
|
||||
|
||||
## Relations
|
||||
- enhances [[Obsidian Integration]] (Using Basic Memory with Obsidian)
|
||||
- visualizes [[Knowledge Format]] (The structure of your knowledge)
|
||||
- complements [[User Guide]] (Ways to use Basic Memory)
|
||||
@@ -1,335 +0,0 @@
|
||||
# Claude.ai Integration Guide
|
||||
|
||||
This guide explains how to connect Basic Memory to Claude.ai, enabling Claude to read and write to your personal knowledge base.
|
||||
|
||||
## Overview
|
||||
|
||||
When connected to Claude.ai, Basic Memory provides:
|
||||
- Persistent memory across conversations
|
||||
- Knowledge graph navigation
|
||||
- Note-taking and search capabilities
|
||||
- File organization and management
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Basic Memory MCP server with OAuth enabled
|
||||
2. Public HTTPS URL (or tunneling service for testing)
|
||||
3. Claude.ai account (Free, Pro, or Enterprise)
|
||||
|
||||
## Quick Start (Testing)
|
||||
|
||||
### 1. Start MCP Server with OAuth
|
||||
|
||||
```bash
|
||||
# Enable OAuth with basic provider
|
||||
export FASTMCP_AUTH_ENABLED=true
|
||||
export FASTMCP_AUTH_PROVIDER=basic
|
||||
|
||||
# Start server on all interfaces
|
||||
basic-memory mcp --transport streamable-http --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
### 2. Make Server Accessible
|
||||
|
||||
For testing, use ngrok:
|
||||
|
||||
```bash
|
||||
# Install ngrok
|
||||
brew install ngrok # macOS
|
||||
# or download from https://ngrok.com
|
||||
|
||||
# Create tunnel
|
||||
ngrok http 8000
|
||||
```
|
||||
|
||||
Note the HTTPS URL (e.g., `https://abc123.ngrok.io`)
|
||||
|
||||
### 3. Register OAuth Client
|
||||
|
||||
```bash
|
||||
# Register a client for Claude
|
||||
basic-memory auth register-client --client-id claude-ai
|
||||
|
||||
# Save the credentials!
|
||||
# Client ID: claude-ai
|
||||
# Client Secret: xxx...
|
||||
```
|
||||
|
||||
### 4. Connect in Claude.ai
|
||||
|
||||
1. Go to Claude.ai → Settings → Integrations
|
||||
2. Click "Add More"
|
||||
3. Enter your server URL: `https://abc123.ngrok.io/mcp`
|
||||
4. Click "Connect"
|
||||
5. Authorize the connection
|
||||
|
||||
### 5. Use in Conversations
|
||||
|
||||
- Click the tools icon (🔧) in the chat
|
||||
- Select "Basic Memory"
|
||||
- Try commands like:
|
||||
- "Create a note about our meeting"
|
||||
- "Search for project ideas"
|
||||
- "Show recent notes"
|
||||
|
||||
## Production Setup
|
||||
|
||||
### 1. Deploy with Supabase Auth
|
||||
|
||||
```bash
|
||||
# .env file
|
||||
FASTMCP_AUTH_ENABLED=true
|
||||
FASTMCP_AUTH_PROVIDER=supabase
|
||||
SUPABASE_URL=https://your-project.supabase.co
|
||||
SUPABASE_ANON_KEY=your-anon-key
|
||||
SUPABASE_SERVICE_KEY=your-service-key
|
||||
```
|
||||
|
||||
### 2. Deploy to Cloud
|
||||
|
||||
Options for deployment:
|
||||
|
||||
#### Vercel
|
||||
```json
|
||||
// vercel.json
|
||||
{
|
||||
"functions": {
|
||||
"api/mcp.py": {
|
||||
"runtime": "python3.9"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Railway
|
||||
```bash
|
||||
# Install Railway CLI
|
||||
brew install railway
|
||||
|
||||
# Deploy
|
||||
railway init
|
||||
railway up
|
||||
```
|
||||
|
||||
#### Docker
|
||||
```dockerfile
|
||||
FROM python:3.12
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN pip install -e .
|
||||
CMD ["basic-memory", "mcp", "--transport", "streamable-http"]
|
||||
```
|
||||
|
||||
### 3. Configure for Organization
|
||||
|
||||
For Claude.ai Enterprise:
|
||||
|
||||
1. **Admin Setup**:
|
||||
- Go to Organizational Settings
|
||||
- Navigate to Integrations
|
||||
- Add MCP server URL for all users
|
||||
- Configure allowed scopes
|
||||
|
||||
2. **User Permissions**:
|
||||
- Users connect individually
|
||||
- Each user has their own auth token
|
||||
- Scopes determine access level
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### 1. Use HTTPS
|
||||
- Required for OAuth
|
||||
- Encrypt all data in transit
|
||||
- Use proper SSL certificates
|
||||
|
||||
### 2. Implement Scopes
|
||||
```bash
|
||||
# Configure required scopes
|
||||
FASTMCP_AUTH_REQUIRED_SCOPES=read,write
|
||||
|
||||
# User-specific scopes
|
||||
read: Can search and read notes
|
||||
write: Can create and update notes
|
||||
admin: Can manage all data
|
||||
```
|
||||
|
||||
### 3. Token Security
|
||||
- Short-lived access tokens (1 hour)
|
||||
- Refresh token rotation
|
||||
- Secure token storage
|
||||
|
||||
### 4. Rate Limiting
|
||||
```python
|
||||
# In your MCP server
|
||||
from fastapi import HTTPException
|
||||
from slowapi import Limiter
|
||||
|
||||
limiter = Limiter(key_func=get_remote_address)
|
||||
|
||||
@app.get("/mcp")
|
||||
@limiter.limit("100/minute")
|
||||
async def mcp_endpoint():
|
||||
# Handle MCP requests
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### 1. Custom Tools
|
||||
|
||||
Create specialized tools for Claude:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
async def analyze_notes(topic: str) -> str:
|
||||
"""Analyze all notes on a specific topic."""
|
||||
# Search and analyze implementation
|
||||
return analysis
|
||||
```
|
||||
|
||||
### 2. Context Preservation
|
||||
|
||||
Use memory:// URLs to maintain context:
|
||||
|
||||
```python
|
||||
@mcp.tool()
|
||||
async def continue_conversation(memory_url: str) -> str:
|
||||
"""Continue from a previous conversation."""
|
||||
context = await build_context(memory_url)
|
||||
return context
|
||||
```
|
||||
|
||||
### 3. Multi-User Support
|
||||
|
||||
With Supabase, each user has isolated data:
|
||||
|
||||
```sql
|
||||
-- Row-level security
|
||||
CREATE POLICY "Users see own notes"
|
||||
ON notes FOR SELECT
|
||||
USING (auth.uid() = user_id);
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
|
||||
1. **"Failed to connect"**
|
||||
- Verify server is running
|
||||
- Check HTTPS is working
|
||||
- Confirm OAuth is enabled
|
||||
|
||||
2. **"Authorization failed"**
|
||||
- Check client credentials
|
||||
- Verify redirect URLs
|
||||
- Review OAuth logs
|
||||
|
||||
3. **"No tools available"**
|
||||
- Ensure MCP tools are registered
|
||||
- Check required scopes
|
||||
- Verify transport type
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable detailed logging:
|
||||
|
||||
```bash
|
||||
# Server side
|
||||
export FASTMCP_LOG_LEVEL=DEBUG
|
||||
export LOGURU_LEVEL=DEBUG
|
||||
|
||||
# Check logs
|
||||
tail -f logs/mcp.log
|
||||
```
|
||||
|
||||
### Test Connection
|
||||
|
||||
```bash
|
||||
# Test OAuth flow
|
||||
curl https://your-server.com/mcp/.well-known/oauth-authorization-server
|
||||
|
||||
# Should return OAuth metadata
|
||||
{
|
||||
"issuer": "https://your-server.com",
|
||||
"authorization_endpoint": "https://your-server.com/auth/authorize",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Regular Backups**
|
||||
- Export your knowledge base
|
||||
- Use version control
|
||||
- Multiple storage locations
|
||||
|
||||
2. **Access Control**
|
||||
- Principle of least privilege
|
||||
- Regular token rotation
|
||||
- Audit access logs
|
||||
|
||||
3. **Performance**
|
||||
- Index frequently searched fields
|
||||
- Optimize large knowledge bases
|
||||
- Use caching where appropriate
|
||||
|
||||
4. **User Experience**
|
||||
- Clear tool descriptions
|
||||
- Helpful error messages
|
||||
- Quick response times
|
||||
|
||||
## Examples
|
||||
|
||||
### Creating Notes
|
||||
|
||||
```
|
||||
User: Create a note about the meeting with the product team
|
||||
|
||||
Claude: I'll create a note about your meeting with the product team.
|
||||
|
||||
[Uses write_note tool]
|
||||
|
||||
Note created: "Meeting with Product Team - 2024-01-15"
|
||||
Location: Work/Meetings/
|
||||
|
||||
I've documented the meeting notes. The note includes the date, attendees, and key discussion points.
|
||||
```
|
||||
|
||||
### Searching Knowledge
|
||||
|
||||
```
|
||||
User: What did we discuss about the API redesign?
|
||||
|
||||
Claude: Let me search for information about the API redesign.
|
||||
|
||||
[Uses search_notes tool]
|
||||
|
||||
I found 3 relevant notes about the API redesign:
|
||||
|
||||
1. "API Redesign Proposal" (2024-01-10)
|
||||
- RESTful architecture
|
||||
- Version 2.0 specifications
|
||||
- Migration timeline
|
||||
|
||||
2. "Technical Review: API Changes" (2024-01-12)
|
||||
- Breaking changes documented
|
||||
- Backwards compatibility plan
|
||||
|
||||
3. "Meeting: API Implementation" (2024-01-14)
|
||||
- Team assignments
|
||||
- Q1 deliverables
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Set up production deployment
|
||||
2. Configure organizational access
|
||||
3. Create custom tools for your workflow
|
||||
4. Implement advanced security features
|
||||
5. Monitor usage and performance
|
||||
|
||||
## Resources
|
||||
|
||||
- [Basic Memory Documentation](../README.md)
|
||||
- [OAuth Setup Guide](OAuth%20Authentication.md)
|
||||
- [MCP Specification](https://modelcontextprotocol.io)
|
||||
- [Claude.ai Help Center](https://support.anthropic.com)
|
||||
@@ -0,0 +1,365 @@
|
||||
# Docker Setup Guide
|
||||
|
||||
Basic Memory can be run in Docker containers to provide a consistent, isolated environment for your knowledge management
|
||||
system. This is particularly useful for integrating with existing Dockerized MCP servers or for deployment scenarios.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: Using Pre-built Images (Recommended)
|
||||
|
||||
Basic Memory provides pre-built Docker images on GitHub Container Registry that are automatically updated with each release.
|
||||
|
||||
1. **Use the official image directly:**
|
||||
```bash
|
||||
docker run -d \
|
||||
--name basic-memory-server \
|
||||
-p 8000:8000 \
|
||||
-v /path/to/your/obsidian-vault:/app/data:rw \
|
||||
-v basic-memory-config:/app/.basic-memory:rw \
|
||||
ghcr.io/basicmachines-co/basic-memory:latest
|
||||
```
|
||||
|
||||
2. **Or use Docker Compose with the pre-built image:**
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
basic-memory:
|
||||
image: ghcr.io/basicmachines-co/basic-memory:latest
|
||||
container_name: basic-memory-server
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- /path/to/your/obsidian-vault:/app/data:rw
|
||||
- basic-memory-config:/app/.basic-memory:rw
|
||||
environment:
|
||||
- BASIC_MEMORY_DEFAULT_PROJECT=main
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Option 2: Using Docker Compose (Building Locally)
|
||||
|
||||
1. **Clone the repository:**
|
||||
```bash
|
||||
git clone https://github.com/basicmachines-co/basic-memory.git
|
||||
cd basic-memory
|
||||
```
|
||||
|
||||
2. **Update the docker-compose.yml:**
|
||||
Edit the volume mount to point to your Obsidian vault:
|
||||
```yaml
|
||||
volumes:
|
||||
# Change './obsidian-vault' to your actual directory path
|
||||
- /path/to/your/obsidian-vault:/app/data:rw
|
||||
```
|
||||
|
||||
3. **Start the container:**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Option 3: Using Docker CLI
|
||||
|
||||
```bash
|
||||
# Build the image
|
||||
docker build -t basic-memory .
|
||||
|
||||
# Run with volume mounting
|
||||
docker run -d \
|
||||
--name basic-memory-server \
|
||||
-v /path/to/your/obsidian-vault:/app/data:rw \
|
||||
-v basic-memory-config:/app/.basic-memory:rw \
|
||||
-e BASIC_MEMORY_DEFAULT_PROJECT=main \
|
||||
basic-memory
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Volume Mounts
|
||||
|
||||
Basic Memory requires several volume mounts for proper operation:
|
||||
|
||||
1. **Knowledge Directory** (Required):
|
||||
```yaml
|
||||
- /path/to/your/obsidian-vault:/app/data:rw
|
||||
```
|
||||
Mount your Obsidian vault or knowledge base directory.
|
||||
|
||||
2. **Configuration and Database** (Recommended):
|
||||
```yaml
|
||||
- basic-memory-config:/app/.basic-memory:rw
|
||||
```
|
||||
Persistent storage for configuration and SQLite database.
|
||||
|
||||
You can edit the basic-memory config.json file located in the /app/.basic-memory/config.json after Basic Memory starts.
|
||||
|
||||
3. **Multiple Projects** (Optional):
|
||||
```yaml
|
||||
- /path/to/project1:/app/data/project1:rw
|
||||
- /path/to/project2:/app/data/project2:rw
|
||||
```
|
||||
|
||||
You can edit the basic-memory config.json file located in the /app/.basic-memory/config.json
|
||||
|
||||
## CLI Commands via Docker
|
||||
|
||||
You can run Basic Memory CLI commands inside the container using `docker exec`:
|
||||
|
||||
### Basic Commands
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
docker exec basic-memory-server basic-memory status
|
||||
|
||||
# Sync files
|
||||
docker exec basic-memory-server basic-memory sync
|
||||
|
||||
# Show help
|
||||
docker exec basic-memory-server basic-memory --help
|
||||
```
|
||||
|
||||
### Managing Projects with Volume Mounts
|
||||
|
||||
When using Docker volumes, you'll need to configure projects to point to your mounted directories:
|
||||
|
||||
1. **Check current configuration:**
|
||||
```bash
|
||||
docker exec basic-memory-server cat /app/.basic-memory/config.json
|
||||
```
|
||||
|
||||
2. **Add a project for your mounted volume:**
|
||||
```bash
|
||||
# If you mounted /path/to/your/vault to /app/data
|
||||
docker exec basic-memory-server basic-memory project create my-vault /app/data
|
||||
|
||||
# Set it as default
|
||||
docker exec basic-memory-server basic-memory project set-default my-vault
|
||||
```
|
||||
|
||||
3. **Sync the new project:**
|
||||
```bash
|
||||
docker exec basic-memory-server basic-memory sync
|
||||
```
|
||||
|
||||
### Example: Setting up an Obsidian Vault
|
||||
|
||||
If you mounted your Obsidian vault like this in docker-compose.yml:
|
||||
```yaml
|
||||
volumes:
|
||||
- /Users/yourname/Documents/ObsidianVault:/app/data:rw
|
||||
```
|
||||
|
||||
Then configure it:
|
||||
```bash
|
||||
# Create project pointing to mounted vault
|
||||
docker exec basic-memory-server basic-memory project create obsidian /app/data
|
||||
|
||||
# Set as default
|
||||
docker exec basic-memory-server basic-memory project set-default obsidian
|
||||
|
||||
# Sync to index all files
|
||||
docker exec basic-memory-server basic-memory sync
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Configure Basic Memory using environment variables:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
|
||||
# Default project
|
||||
- BASIC_MEMORY_DEFAULT_PROJECT=main
|
||||
|
||||
# Enable real-time sync
|
||||
- BASIC_MEMORY_SYNC_CHANGES=true
|
||||
|
||||
# Logging level
|
||||
- BASIC_MEMORY_LOG_LEVEL=INFO
|
||||
|
||||
# Sync delay in milliseconds
|
||||
- BASIC_MEMORY_SYNC_DELAY=1000
|
||||
```
|
||||
|
||||
## File Permissions
|
||||
|
||||
### Linux/macOS
|
||||
|
||||
The Docker container now runs as a non-root user to avoid file ownership issues. By default, the container uses UID/GID 1000, but you can customize this to match your user:
|
||||
|
||||
```bash
|
||||
# Build with custom UID/GID to match your user
|
||||
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t basic-memory .
|
||||
|
||||
# Or use docker-compose with build args
|
||||
```
|
||||
|
||||
**Example docker-compose.yml with custom user:**
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
basic-memory:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
UID: 1000 # Replace with your UID
|
||||
GID: 1000 # Replace with your GID
|
||||
container_name: basic-memory-server
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- /path/to/your/obsidian-vault:/app/data:rw
|
||||
- basic-memory-config:/app/.basic-memory:rw
|
||||
environment:
|
||||
- BASIC_MEMORY_DEFAULT_PROJECT=main
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
**Using pre-built images:**
|
||||
If using the pre-built image from GitHub Container Registry, files will be created with UID/GID 1000. You can either:
|
||||
|
||||
1. Change your local directory ownership to match:
|
||||
```bash
|
||||
sudo chown -R 1000:1000 /path/to/your/obsidian-vault
|
||||
```
|
||||
|
||||
2. Or build your own image with custom UID/GID as shown above.
|
||||
|
||||
### Windows
|
||||
|
||||
When using Docker Desktop on Windows, ensure the directories are shared:
|
||||
|
||||
1. Open Docker Desktop
|
||||
2. Go to Settings → Resources → File Sharing
|
||||
3. Add your knowledge directory path
|
||||
4. Apply & Restart
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **File Watching Not Working:**
|
||||
- Ensure volume mounts are read-write (`:rw`)
|
||||
- Check directory permissions
|
||||
- On Linux, may need to increase inotify limits:
|
||||
```bash
|
||||
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
2. **Configuration Not Persisting:**
|
||||
- Use named volumes for `/app/.basic-memory`
|
||||
- Check volume mount permissions
|
||||
|
||||
3. **Network Connectivity:**
|
||||
- For HTTP transport, ensure port 8000 is exposed
|
||||
- Check firewall settings
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Run with debug logging:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- BASIC_MEMORY_LOG_LEVEL=DEBUG
|
||||
```
|
||||
|
||||
View logs:
|
||||
|
||||
```bash
|
||||
docker-compose logs -f basic-memory
|
||||
```
|
||||
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Docker Security:**
|
||||
The container runs as a non-root user (UID/GID 1000 by default) for improved security. You can customize the user ID using build arguments to match your local user.
|
||||
|
||||
2. **Volume Permissions:**
|
||||
Ensure mounted directories have appropriate permissions and don't expose sensitive data. With the non-root container, files will be created with the specified user ownership.
|
||||
|
||||
3. **Network Security:**
|
||||
If using HTTP transport, consider using reverse proxy with SSL/TLS and authentication if the endpoint is available on
|
||||
a network.
|
||||
|
||||
4. **IMPORTANT:** The HTTP endpoints have no authorization. They should not be exposed on a public network.
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Claude Desktop with Docker
|
||||
|
||||
The recommended way to connect Claude Desktop to the containerized Basic Memory is using `mcp-proxy`, which converts the HTTP transport to STDIO that Claude Desktop expects:
|
||||
|
||||
1. **Start the Docker container:**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
2. **Configure Claude Desktop** to use mcp-proxy:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"basic-memory": {
|
||||
"command": "uvx",
|
||||
"args": [
|
||||
"mcp-proxy",
|
||||
"http://localhost:8000/mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
For Docker-specific issues:
|
||||
|
||||
1. Check the [troubleshooting section](#troubleshooting) above
|
||||
2. Review container logs: `docker-compose logs basic-memory`
|
||||
3. Verify volume mounts: `docker inspect basic-memory-server`
|
||||
4. Test file permissions: `docker exec basic-memory-server ls -la /app`
|
||||
|
||||
For general Basic Memory support, see the main [README](../README.md)
|
||||
and [documentation](https://memory.basicmachines.co/).
|
||||
|
||||
## GitHub Container Registry Images
|
||||
|
||||
### Available Images
|
||||
|
||||
Pre-built Docker images are available on GitHub Container Registry at [`ghcr.io/basicmachines-co/basic-memory`](https://github.com/basicmachines-co/basic-memory/pkgs/container/basic-memory).
|
||||
|
||||
**Supported architectures:**
|
||||
- `linux/amd64` (Intel/AMD x64)
|
||||
- `linux/arm64` (ARM64, including Apple Silicon)
|
||||
|
||||
**Available tags:**
|
||||
- `latest` - Latest stable release
|
||||
- `v0.13.8`, `v0.13.7`, etc. - Specific version tags
|
||||
- `v0.13`, `v0.12`, etc. - Major.minor tags
|
||||
|
||||
### Automated Builds
|
||||
|
||||
Docker images are automatically built and published when new releases are tagged:
|
||||
|
||||
1. **Release Process:** When a git tag matching `v*` (e.g., `v0.13.8`) is pushed, the CI workflow automatically:
|
||||
- Builds multi-platform Docker images
|
||||
- Pushes to GitHub Container Registry with appropriate tags
|
||||
- Uses native GitHub integration for seamless publishing
|
||||
|
||||
2. **CI/CD Pipeline:** The Docker workflow includes:
|
||||
- Multi-platform builds (AMD64 and ARM64)
|
||||
- Layer caching for faster builds
|
||||
- Automatic tagging with semantic versioning
|
||||
- Security scanning and optimization
|
||||
|
||||
### Setup Requirements (For Maintainers)
|
||||
|
||||
GitHub Container Registry integration is automatic for this repository:
|
||||
|
||||
1. **No external setup required** - GHCR is natively integrated with GitHub
|
||||
2. **Automatic permissions** - Uses `GITHUB_TOKEN` with `packages: write` permission
|
||||
3. **Public by default** - Images are automatically public for public repositories
|
||||
|
||||
The Docker CI workflow (`.github/workflows/docker.yml`) handles everything automatically when version tags are pushed.
|
||||
@@ -1,355 +0,0 @@
|
||||
---
|
||||
title: Getting Started with Basic Memory
|
||||
type: note
|
||||
permalink: docs/getting-started
|
||||
---
|
||||
|
||||
# Getting Started with Basic Memory
|
||||
|
||||
This guide will help you install Basic Memory, configure it with Claude Desktop, and create your first knowledge notes
|
||||
through conversations.
|
||||
|
||||
Basic Memory uses the [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) to connect with LLMs.
|
||||
It can be used with any service that supports the MCP, but Claude Desktop works especially well.
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
The easiest way to install basic memory is via `uv`. See the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/).
|
||||
|
||||
### 1. Install Basic Memory
|
||||
|
||||
**v0.13.0 offers multiple installation options:**
|
||||
|
||||
```bash
|
||||
# Stable release (recommended)
|
||||
uv tool install basic-memory
|
||||
# or: pip install basic-memory
|
||||
|
||||
# Beta releases (new features, testing)
|
||||
pip install basic-memory --pre
|
||||
|
||||
# Development builds (latest changes)
|
||||
pip install basic-memory --pre --force-reinstall
|
||||
```
|
||||
|
||||
**Version Information:**
|
||||
- **Stable**: Latest tested release (e.g., `0.13.0`)
|
||||
- **Beta**: Pre-release versions (e.g., `0.13.0b1`)
|
||||
- **Development**: Auto-published from git commits (e.g., `0.12.4.dev26+468a22f`)
|
||||
|
||||
> **Important**: You need to install Basic Memory using one of the commands above to use the command line tools.
|
||||
|
||||
Using `uv tool install` will install the basic-memory package in a standalone virtual environment. See the [UV docs](https://docs.astral.sh/uv/concepts/tools/) for more info.
|
||||
|
||||
### 2. Configure Claude Desktop
|
||||
|
||||
Edit your Claude Desktop config, located at `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"basic-memory": {
|
||||
"command": "uvx",
|
||||
"args": [
|
||||
"basic-memory",
|
||||
"mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Restart Claude Desktop**. You should see Basic Memory tools available in the "tools" menu in Claude Desktop (the little hammer icon in the bottom-right corner of the chat interface). Click it to view available tools.
|
||||
#### Fix Path to uv
|
||||
|
||||
If you get an error that says `ENOENT` , this most likely means Claude Desktop could not find your `uv` installation. Make sure that you have `uv` installed per the instructions above, then:
|
||||
|
||||
**Step 1: Find the absolute path to uvx**
|
||||
|
||||
Open Terminal and run:
|
||||
|
||||
```bash
|
||||
which uvx
|
||||
```
|
||||
|
||||
This will show you the full path (e.g., `/Users/yourusername/.cargo/bin/uvx`).
|
||||
|
||||
**Step 2: Edit Claude Desktop Configuration**
|
||||
|
||||
Edit the Claude Desktop config:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"basic-memory": {
|
||||
"command": "/absolute/path/to/uvx",
|
||||
"args": [
|
||||
"basic-memory",
|
||||
"mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace `/absolute/path/to/uvx` with the actual path you found in Step 1.
|
||||
|
||||
**Step 3: Restart Claude Desktop**
|
||||
|
||||
Close and reopen Claude Desktop for the changes to take effect.
|
||||
|
||||
### 3. Sync changes in real time
|
||||
|
||||
> **Note**: The service will sync changes from your project directory in real time so they available for the AI assistant.
|
||||
|
||||
To disable realtime sync, you can update the config. See [[CLI Reference#sync]].
|
||||
### 4. Staying Updated
|
||||
|
||||
To update Basic Memory when new versions are released:
|
||||
|
||||
```bash
|
||||
# Update stable release
|
||||
uv tool upgrade basic-memory
|
||||
# or: pip install --upgrade basic-memory
|
||||
|
||||
# Update to latest beta (v0.13.0)
|
||||
pip install --upgrade basic-memory --pre
|
||||
|
||||
# Get latest development build
|
||||
pip install --upgrade basic-memory --pre --force-reinstall
|
||||
```
|
||||
|
||||
**v0.13.0 Update Benefits:**
|
||||
- **Fluid project switching** during conversations
|
||||
- **Advanced note editing** capabilities
|
||||
- **Smart file management** with move operations
|
||||
- **Enhanced search** with frontmatter tag support
|
||||
|
||||
> **Note**: After updating, restart Claude Desktop for changes to take effect. No sync restart needed in v0.13.0.
|
||||
|
||||
### 5. Multi-Project Setup (Enhanced in v0.13.0)
|
||||
|
||||
By default, Basic Memory creates a project in `~/basic-memory`. v0.13.0 introduces **fluid project management** - switch between projects instantly during conversations.
|
||||
|
||||
```
|
||||
# Create a new project
|
||||
basic-memory project create work ~/work-basic-memory
|
||||
|
||||
# Set the default project
|
||||
basic-memory project set-default work
|
||||
|
||||
# List all projects with status
|
||||
basic-memory project list
|
||||
|
||||
# Get detailed project information
|
||||
basic-memory project info
|
||||
```
|
||||
|
||||
**New in v0.13.0:**
|
||||
- **Instant switching**: Change projects during conversations without restart
|
||||
- **Unified database**: All projects in single `~/.basic-memory/memory.db`
|
||||
- **Better performance**: Optimized queries and reduced file I/O
|
||||
- **Session context**: Maintains active project throughout conversations
|
||||
|
||||
## Troubleshooting Installation
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Claude Says "No Basic Memory Tools Available"
|
||||
|
||||
If Claude cannot find Basic Memory tools:
|
||||
|
||||
1. **Check absolute paths**: Ensure you're using complete absolute paths to uvx in the Claude Desktop configuration
|
||||
2. **Verify installation**: Run `basic-memory --version` in Terminal to confirm Basic Memory is installed
|
||||
3. **Restart applications**: Restart both Terminal and Claude Desktop after making configuration changes
|
||||
4. **Check sync status**: You can view the sync status by running `basic-memory status
|
||||
.
|
||||
#### Permission Issues
|
||||
|
||||
If you encounter permission errors:
|
||||
|
||||
1. Check that Basic Memory has access to create files in your home directory
|
||||
2. Ensure Claude Desktop has permission to execute the uvx command
|
||||
|
||||
## Creating Your First Knowledge Note
|
||||
|
||||
1. **Open Claude Desktop** and start a new conversation.
|
||||
|
||||
2. **Have a natural conversation** about any topic:
|
||||
```
|
||||
You: "Let's talk about coffee brewing methods I've been experimenting with."
|
||||
Claude: "I'd be happy to discuss coffee brewing methods..."
|
||||
You: "I've found that pour over gives more flavor clarity than French press..."
|
||||
```
|
||||
|
||||
3. **Ask Claude to create a note**:
|
||||
```
|
||||
You: "Could you create a note summarizing what we've discussed about coffee brewing?"
|
||||
```
|
||||
|
||||
4. **Confirm note creation**:
|
||||
Claude will confirm when the note has been created and where it's stored.
|
||||
|
||||
5. **View the created file** in your `~/basic-memory` directory using any text editor or Obsidian.
|
||||
The file structure will look similar to:
|
||||
```markdown
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
permalink: coffee-brewing-methods
|
||||
tags: [coffee, brewing, equipment] # v0.13.0: Now searchable!
|
||||
---
|
||||
|
||||
# Coffee Brewing Methods
|
||||
|
||||
## Observations
|
||||
- [method] Pour over provides more clarity...
|
||||
- [technique] Water temperature at 205°F...
|
||||
|
||||
## Relations
|
||||
- relates_to [[Other Coffee Topics]]
|
||||
```
|
||||
|
||||
**v0.13.0 Improvements:**
|
||||
- **Real-time sync**: Changes appear immediately, no background sync needed
|
||||
- **Searchable tags**: Frontmatter tags are now indexed for search
|
||||
- **Better file organization**: Enhanced file management capabilities
|
||||
|
||||
## Using Special Prompts
|
||||
|
||||
Basic Memory includes special prompts that help you start conversations with context from your knowledge base:
|
||||
|
||||
### Continue Conversation
|
||||
|
||||
To resume a previous topic:
|
||||
|
||||
```
|
||||
You: "Let's continue our conversation about coffee brewing."
|
||||
```
|
||||
|
||||
This prompt triggers Claude to:
|
||||
|
||||
1. Search your knowledge base for relevant content about coffee brewing
|
||||
2. Build context from these documents
|
||||
3. Resume the conversation with full awareness of previous discussions
|
||||
|
||||
### Recent Activity
|
||||
|
||||
To see what you've been working on:
|
||||
|
||||
```
|
||||
You: "What have we been discussing recently?"
|
||||
```
|
||||
|
||||
This prompt causes Claude to:
|
||||
|
||||
1. Retrieve documents modified in the recent past
|
||||
2. Summarize the topics and main points
|
||||
3. Offer to continue any of those discussions
|
||||
|
||||
### Search
|
||||
|
||||
To find specific information:
|
||||
|
||||
```
|
||||
You: "Find information about pour over coffee methods."
|
||||
```
|
||||
|
||||
Claude will:
|
||||
|
||||
1. Search your knowledge base for relevant documents
|
||||
2. Summarize the key findings
|
||||
3. Offer to explore specific documents in more detail
|
||||
|
||||
See [[User Guide#Using Special Prompts]] for further information.
|
||||
|
||||
## Using Your Knowledge Base
|
||||
|
||||
### Referencing Knowledge
|
||||
|
||||
In future conversations, reference your existing knowledge:
|
||||
|
||||
```
|
||||
You: "What water temperature did we decide was optimal for coffee brewing?"
|
||||
```
|
||||
|
||||
Or directly reference notes using memory:// URLs:
|
||||
|
||||
```
|
||||
You: "Take a look at memory://coffee-brewing-methods and let's discuss how to improve my technique."
|
||||
```
|
||||
|
||||
### Building On Previous Knowledge (Enhanced in v0.13.0)
|
||||
|
||||
Basic Memory enables continuous knowledge building:
|
||||
|
||||
1. **Reference previous discussions** in new conversations
|
||||
2. **Edit notes incrementally** without rewriting entire documents
|
||||
3. **Move and organize notes** as your knowledge base grows
|
||||
4. **Switch between projects** instantly during conversations
|
||||
5. **Search by tags** to find related content quickly
|
||||
6. **Create connections** between related topics
|
||||
7. **Follow relationships** to build comprehensive context
|
||||
|
||||
### v0.13.0 Workflow Examples
|
||||
|
||||
**Incremental Editing:**
|
||||
```
|
||||
You: "Add a section about espresso to my coffee brewing notes"
|
||||
Claude: [Uses edit_note to append new section]
|
||||
```
|
||||
|
||||
**File Organization:**
|
||||
```
|
||||
You: "Move my old meeting notes to an archive folder"
|
||||
Claude: [Uses move_note with database consistency]
|
||||
```
|
||||
|
||||
**Project Switching:**
|
||||
```
|
||||
You: "Switch to my work project and show recent activity"
|
||||
Claude: [Switches projects and shows work-specific content]
|
||||
```
|
||||
|
||||
## Importing Existing Conversations
|
||||
|
||||
Import your existing AI conversations:
|
||||
|
||||
```bash
|
||||
# From Claude
|
||||
basic-memory import claude conversations
|
||||
|
||||
# From ChatGPT
|
||||
basic-memory import chatgpt
|
||||
```
|
||||
|
||||
After importing, changes sync automatically in real-time. You can see project statistics by running `basic-memory project info`.
|
||||
|
||||
## Quick Tips
|
||||
|
||||
### General Usage
|
||||
- Basic Memory syncs changes in real-time (no manual sync needed)
|
||||
- Use special prompts (Continue Conversation, Recent Activity, Search) to start contextual discussions
|
||||
- Build connections between notes for a richer knowledge graph
|
||||
- Use direct `memory://` URLs with permalinks for precise context
|
||||
- Review and edit AI-generated notes for accuracy
|
||||
|
||||
### v0.13.0 Features
|
||||
- **Switch projects instantly**: "Switch to my work project" - no restart needed
|
||||
- **Edit notes incrementally**: "Add a section about..." instead of rewriting
|
||||
- **Organize with moves**: "Move this to my archive folder" with database consistency
|
||||
- **Search by tags**: Frontmatter tags are now searchable
|
||||
- **Try beta builds**: `pip install basic-memory --pre` for latest features
|
||||
|
||||
## Next Steps
|
||||
|
||||
After getting started, explore these areas:
|
||||
|
||||
1. **Read the [[User Guide]]** for comprehensive usage instructions
|
||||
2. **Understand the [[Knowledge Format]]** to learn how knowledge is structured
|
||||
3. **Set up [[Obsidian Integration]]** for visual knowledge navigation
|
||||
4. **Learn about [[Canvas]]** visualizations for mapping concepts
|
||||
5. **Review the [[CLI Reference]]** for command line tools
|
||||
6. **Explore [[OAuth Authentication Guide]]** for secure remote access (v0.13.0)
|
||||
7. **Set up multiple projects** for different knowledge areas (v0.13.0)
|
||||
@@ -1,207 +0,0 @@
|
||||
---
|
||||
title: Knowledge Format
|
||||
type: note
|
||||
permalink: docs/knowledge-format
|
||||
tags:
|
||||
- architecture
|
||||
- patterns
|
||||
- knowledge
|
||||
- design
|
||||
---
|
||||
|
||||
# Knowledge Format
|
||||
|
||||
Basic Memory uses standard Markdown with simple semantic patterns to create a knowledge graph. This document details the file structure and patterns used to organize knowledge.
|
||||
|
||||
## File-First Architecture
|
||||
|
||||
All knowledge in Basic Memory is stored in plain text Markdown files:
|
||||
|
||||
- Files are the source of truth for all knowledge
|
||||
- Changes to files automatically update the knowledge graph
|
||||
- You maintain complete ownership and control
|
||||
- Files work with git and other version control systems
|
||||
- Knowledge persists independently of any AI conversation
|
||||
|
||||
## Core Document Structure
|
||||
|
||||
Every document uses this basic structure:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Document Title
|
||||
type: note
|
||||
tags: [tag1, tag2]
|
||||
permalink: custom-path
|
||||
---
|
||||
|
||||
# Document Title
|
||||
|
||||
Regular markdown content...
|
||||
|
||||
## Observations
|
||||
- [category] Content with #tags (optional context)
|
||||
|
||||
## Relations
|
||||
- relation_type [[Other Document]] (optional context)
|
||||
```
|
||||
|
||||
### Frontmatter
|
||||
|
||||
The YAML frontmatter at the top of each file defines essential metadata:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Document Title # Used for linking and references
|
||||
type: note # Document type
|
||||
tags: [tag1, tag2] # For organization and searching
|
||||
permalink: custom-link # Optional custom URL path
|
||||
---
|
||||
```
|
||||
|
||||
The title is particularly important as it's used to create links between documents.
|
||||
|
||||
### Observations
|
||||
|
||||
Observations are facts or statements about a topic:
|
||||
|
||||
```markdown
|
||||
## Observations
|
||||
- [tech] Uses SQLite for storage #database
|
||||
- [design] Follows local-first architecture #architecture
|
||||
- [decision] Selected bcrypt for passwords #security (Based on audit)
|
||||
```
|
||||
|
||||
Each observation contains:
|
||||
- **Category** in [brackets] - classifies the information type
|
||||
- **Content text** - the main information
|
||||
- Optional **#tags** - additional categorization
|
||||
- Optional **(context)** - supporting details
|
||||
|
||||
Common categories include:
|
||||
- `[tech]`: Technical details
|
||||
- `[design]`: Architecture decisions
|
||||
- `[feature]`: User capabilities
|
||||
- `[decision]`: Choices that were made
|
||||
- `[principle]`: Fundamental concepts
|
||||
- `[method]`: Approaches or techniques
|
||||
- `[preference]`: Personal opinions
|
||||
|
||||
### Relations
|
||||
|
||||
Relations connect documents to form the knowledge graph:
|
||||
|
||||
```markdown
|
||||
## Relations
|
||||
- implements [[Search Design]]
|
||||
- depends_on [[Database Schema]]
|
||||
- relates_to [[User Interface]]
|
||||
```
|
||||
|
||||
You can also create inline references:
|
||||
```markdown
|
||||
This builds on [[Core Design]] and uses [[Utility Functions]].
|
||||
```
|
||||
|
||||
Common relation types include:
|
||||
- `implements`: Implementation of a specification
|
||||
- `depends_on`: Required dependency
|
||||
- `relates_to`: General connection
|
||||
- `inspired_by`: Source of ideas
|
||||
- `extends`: Enhancement
|
||||
- `part_of`: Component relationship
|
||||
- `contains`: Hierarchical relationship
|
||||
- `pairs_with`: Complementary relationship
|
||||
|
||||
## Knowledge Graph
|
||||
|
||||
Basic Memory automatically builds a knowledge graph from your document connections:
|
||||
|
||||
- Each document becomes a node in the graph
|
||||
- Relations create edges between nodes
|
||||
- Relation types add semantic meaning to connections
|
||||
- Forward references can link to documents that don't exist yet
|
||||
|
||||
This graph enables rich context building and navigation across your knowledge base.
|
||||
|
||||
## Permalinks and memory:// URLs
|
||||
|
||||
Every document in Basic Memory has a unique permalink that serves as its stable identifier:
|
||||
|
||||
### How Permalinks Work
|
||||
|
||||
- **Automatically assigned**: The system generates a permalink for each document
|
||||
- **Based on title**: By default, derived from the document title
|
||||
- **Always unique**: If conflicts exist, the system adds a suffix to ensure uniqueness
|
||||
- **Stable reference**: Remains the same even if the file moves in the directory structure
|
||||
- **Used in memory:// URLs**: Forms the basis of the memory:// addressing scheme
|
||||
|
||||
You can specify a custom permalink in the frontmatter:
|
||||
```yaml
|
||||
---
|
||||
title: Authentication Approaches
|
||||
permalink: auth-approaches-2024
|
||||
---
|
||||
```
|
||||
|
||||
If not specified, one will be generated automatically from the title, if the note has has a frontmatter section.
|
||||
|
||||
By default a notes' permalink value will not change if the file is moved. It's a **stable** identifier :). But if you'd rather permalinks are always updated when a file moves, you can set the config setting in the global config.
|
||||
|
||||
The config file for Basic Memory is in the home directory under `.basic-memory/config.json`.
|
||||
|
||||
To change the behavior, set the following value:
|
||||
```
|
||||
~/.basic-memory/config.json
|
||||
{
|
||||
"update_permalinks_on_move": true
|
||||
}
|
||||
```
|
||||
|
||||
### Using memory:// URLs
|
||||
|
||||
The memory:// URL scheme provides a reliable way to reference knowledge:
|
||||
|
||||
```
|
||||
memory://auth-approaches-2024 # Direct access by permalink
|
||||
memory://Authentication Approaches # Access by title (automatically resolves)
|
||||
memory://project/auth-approaches # Access by path
|
||||
```
|
||||
|
||||
Memory URLs support pattern matching for more powerful queries:
|
||||
|
||||
```
|
||||
memory://auth* # All documents with permalinks starting with "auth"
|
||||
memory://*/approaches # All documents with permalinks ending with "approaches"
|
||||
memory://project/*/requirements # All requirements documents in the project folder
|
||||
memory://docs/search/implements/* # Follow all implements relations from search docs
|
||||
```
|
||||
|
||||
This addressing scheme ensures content remains accessible even as your knowledge base evolves and files are reorganized.
|
||||
|
||||
## File Organization
|
||||
|
||||
Organize files in any structure that suits your needs:
|
||||
|
||||
```
|
||||
docs/
|
||||
architecture/
|
||||
design.md
|
||||
patterns.md
|
||||
features/
|
||||
search.md
|
||||
auth.md
|
||||
```
|
||||
|
||||
You can:
|
||||
- Group by topic in folders
|
||||
- Use a flat structure with descriptive filenames
|
||||
- Tag files for easier discovery
|
||||
- Add custom metadata in frontmatter
|
||||
|
||||
The system will build the semantic knowledge graph regardless of how you organize your files.
|
||||
|
||||
## Relations
|
||||
- implemented_by [[User Guide]] (How to work with this format)
|
||||
- relates_to [[Getting Started with Basic Memory]] (Setup instructions)
|
||||
- explained_in [[Introduction to Basic Memory]] (Overview of the system)
|
||||
@@ -1,259 +0,0 @@
|
||||
# OAuth Authentication Guide
|
||||
|
||||
Basic Memory MCP server supports OAuth 2.1 authentication for secure access control. This guide covers setup, testing, and production deployment.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Enable OAuth
|
||||
|
||||
```bash
|
||||
# Set environment variable
|
||||
export FASTMCP_AUTH_ENABLED=true
|
||||
|
||||
# Or use .env file
|
||||
echo "FASTMCP_AUTH_ENABLED=true" >> .env
|
||||
```
|
||||
|
||||
### 2. Start the Server
|
||||
|
||||
```bash
|
||||
basic-memory mcp --transport streamable-http
|
||||
```
|
||||
|
||||
### 3. Test with MCP Inspector
|
||||
|
||||
Since the basic auth provider uses in-memory storage with per-instance secret keys, you'll need to use a consistent approach:
|
||||
|
||||
#### Option A: Use Environment Variable for Secret Key
|
||||
|
||||
```bash
|
||||
# Set a fixed secret key for testing
|
||||
export FASTMCP_AUTH_SECRET_KEY="your-test-secret-key"
|
||||
|
||||
# Start the server
|
||||
FASTMCP_AUTH_ENABLED=true basic-memory mcp --transport streamable-http
|
||||
|
||||
# In another terminal, register a client
|
||||
basic-memory auth register-client --client-id=test-client
|
||||
|
||||
# Get a token using the same secret key
|
||||
basic-memory auth test-auth
|
||||
```
|
||||
|
||||
#### Option B: Use the Built-in Test Endpoint
|
||||
|
||||
```bash
|
||||
# Start server with OAuth
|
||||
FASTMCP_AUTH_ENABLED=true basic-memory mcp --transport streamable-http
|
||||
|
||||
# Register a client and get token in one step
|
||||
curl -X POST http://localhost:8000/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"client_metadata": {"client_name": "Test Client"}}'
|
||||
|
||||
# Use the returned client_id and client_secret
|
||||
curl -X POST http://localhost:8000/token \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"
|
||||
```
|
||||
|
||||
### 4. Configure MCP Inspector
|
||||
|
||||
1. Open MCP Inspector
|
||||
2. Configure:
|
||||
- Server URL: `http://localhost:8000/mcp/` (note the trailing slash!)
|
||||
- Transport: `streamable-http`
|
||||
- Custom Headers:
|
||||
```
|
||||
Authorization: Bearer YOUR_ACCESS_TOKEN
|
||||
Accept: application/json, text/event-stream
|
||||
```
|
||||
|
||||
## OAuth Endpoints
|
||||
|
||||
The server provides these OAuth endpoints automatically:
|
||||
|
||||
- `GET /authorize` - Authorization endpoint
|
||||
- `POST /token` - Token exchange endpoint
|
||||
- `GET /.well-known/oauth-authorization-server` - OAuth metadata
|
||||
- `POST /register` - Client registration (if enabled)
|
||||
- `POST /revoke` - Token revocation (if enabled)
|
||||
|
||||
## OAuth Flow
|
||||
|
||||
### Standard Authorization Code Flow
|
||||
|
||||
1. **Get Authorization Code**:
|
||||
```bash
|
||||
curl "http://localhost:8000/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8000/callback&response_type=code&code_challenge=YOUR_CHALLENGE&code_challenge_method=S256"
|
||||
```
|
||||
|
||||
2. **Exchange Code for Token**:
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/token \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=authorization_code&code=AUTH_CODE&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&code_verifier=YOUR_VERIFIER"
|
||||
```
|
||||
|
||||
3. **Use Access Token**:
|
||||
```bash
|
||||
curl http://localhost:8000/mcp \
|
||||
-H "Authorization: Bearer ACCESS_TOKEN"
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Using Supabase Auth
|
||||
|
||||
For production, use Supabase for persistent auth storage:
|
||||
|
||||
```bash
|
||||
# Configure environment
|
||||
FASTMCP_AUTH_ENABLED=true
|
||||
FASTMCP_AUTH_PROVIDER=supabase
|
||||
SUPABASE_URL=https://your-project.supabase.co
|
||||
SUPABASE_ANON_KEY=your-anon-key
|
||||
SUPABASE_SERVICE_KEY=your-service-key
|
||||
|
||||
# Start server
|
||||
basic-memory mcp --transport streamable-http --host 0.0.0.0
|
||||
```
|
||||
|
||||
### Security Requirements
|
||||
|
||||
1. **HTTPS Required**: OAuth requires HTTPS in production (localhost exception for testing)
|
||||
2. **PKCE Support**: Claude.ai requires PKCE for authorization
|
||||
3. **Token Expiration**: Access tokens expire after 1 hour
|
||||
4. **Scopes**: Supported scopes are `read`, `write`, and `admin`
|
||||
|
||||
## Connecting from Claude.ai
|
||||
|
||||
1. **Deploy with HTTPS**:
|
||||
```bash
|
||||
# Use ngrok for testing
|
||||
ngrok http 8000
|
||||
|
||||
# Or deploy to cloud provider
|
||||
```
|
||||
|
||||
2. **Configure in Claude.ai**:
|
||||
- Go to Settings → Integrations
|
||||
- Click "Add More"
|
||||
- Enter: `https://your-server.com/mcp`
|
||||
- Click "Connect"
|
||||
- Authorize in the popup window
|
||||
|
||||
## Debugging
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **401 Unauthorized**:
|
||||
- Check token is valid and not expired
|
||||
- Verify secret key consistency
|
||||
- Ensure bearer token format: `Authorization: Bearer TOKEN`
|
||||
|
||||
2. **404 on Auth Endpoints**:
|
||||
- Endpoints are at root, not under `/auth`
|
||||
- Use `/authorize` not `/auth/authorize`
|
||||
|
||||
3. **Token Validation Fails**:
|
||||
- Basic provider uses in-memory storage
|
||||
- Tokens don't persist across server restarts
|
||||
- Use same secret key for testing
|
||||
|
||||
### Debug Commands
|
||||
|
||||
```bash
|
||||
# Check OAuth metadata
|
||||
curl http://localhost:8000/.well-known/oauth-authorization-server
|
||||
|
||||
# Enable debug logging
|
||||
export FASTMCP_LOG_LEVEL=DEBUG
|
||||
|
||||
# Test token directly
|
||||
curl http://localhost:8000/mcp \
|
||||
-H "Authorization: Bearer YOUR_TOKEN" \
|
||||
-v
|
||||
```
|
||||
|
||||
## Provider Options
|
||||
|
||||
- **basic**: In-memory storage (development only)
|
||||
- **supabase**: Recommended for production
|
||||
- **github**: GitHub OAuth integration
|
||||
- **google**: Google OAuth integration
|
||||
|
||||
## Example Test Script
|
||||
|
||||
```python
|
||||
import httpx
|
||||
import asyncio
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
async def test_oauth_flow():
|
||||
"""Test the full OAuth flow"""
|
||||
client_id = "test-client"
|
||||
client_secret = "test-secret"
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
# 1. Get authorization code
|
||||
auth_response = await client.get(
|
||||
"http://localhost:8000/authorize",
|
||||
params={
|
||||
"client_id": client_id,
|
||||
"redirect_uri": "http://localhost:8000/callback",
|
||||
"response_type": "code",
|
||||
"code_challenge": "test-challenge",
|
||||
"code_challenge_method": "S256",
|
||||
"state": "test-state"
|
||||
}
|
||||
)
|
||||
|
||||
# Extract code from redirect URL
|
||||
redirect_url = auth_response.headers.get("Location")
|
||||
parsed = urlparse(redirect_url)
|
||||
code = parse_qs(parsed.query)["code"][0]
|
||||
|
||||
# 2. Exchange for token
|
||||
token_response = await client.post(
|
||||
"http://localhost:8000/token",
|
||||
data={
|
||||
"grant_type": "authorization_code",
|
||||
"code": code,
|
||||
"client_id": client_id,
|
||||
"client_secret": client_secret,
|
||||
"code_verifier": "test-verifier",
|
||||
"redirect_uri": "http://localhost:8000/callback"
|
||||
}
|
||||
)
|
||||
|
||||
tokens = token_response.json()
|
||||
print(f"Access token: {tokens['access_token']}")
|
||||
|
||||
# 3. Test MCP endpoint
|
||||
mcp_response = await client.post(
|
||||
"http://localhost:8000/mcp",
|
||||
headers={"Authorization": f"Bearer {tokens['access_token']}"},
|
||||
json={"method": "initialize", "params": {}}
|
||||
)
|
||||
|
||||
print(f"MCP Response: {mcp_response.status_code}")
|
||||
|
||||
asyncio.run(test_oauth_flow())
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `FASTMCP_AUTH_ENABLED` | Enable OAuth authentication | `false` |
|
||||
| `FASTMCP_AUTH_PROVIDER` | OAuth provider type | `basic` |
|
||||
| `FASTMCP_AUTH_SECRET_KEY` | JWT signing key (basic provider) | Random |
|
||||
| `FASTMCP_AUTH_ISSUER_URL` | OAuth issuer URL | `http://localhost:8000` |
|
||||
| `FASTMCP_AUTH_REQUIRED_SCOPES` | Required scopes (comma-separated) | `read,write` |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Supabase OAuth Setup](./Supabase%20OAuth%20Setup.md) - Production auth setup
|
||||
- [External OAuth Providers](./External%20OAuth%20Providers.md) - GitHub, Google integration
|
||||
- [MCP OAuth Specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) - Official spec
|
||||
@@ -1,146 +0,0 @@
|
||||
---
|
||||
title: Obsidian Integration
|
||||
type: note
|
||||
permalink: docs/obsidian-integration
|
||||
---
|
||||
|
||||
# Obsidian Integration
|
||||
|
||||
Basic Memory integrates seamlessly with [Obsidian](https://obsidian.md), providing powerful visualization and navigation capabilities for your knowledge graph.
|
||||
|
||||
## Setup
|
||||
|
||||
### Creating an Obsidian Vault
|
||||
|
||||
1. Download and install [Obsidian](https://obsidian.md)
|
||||
2. Create a new vault
|
||||
3. Point it to your Basic Memory directory (~/basic-memory by default)
|
||||
4. Enable core plugins like Graph View, Backlinks, and Tags
|
||||
|
||||
## Visualization Features
|
||||
|
||||
### Graph View
|
||||
|
||||
Obsidian's Graph View provides a visual representation of your knowledge network:
|
||||
|
||||
- Each document appears as a node
|
||||
- Relations appear as connections between nodes
|
||||
- Colors can be customized to distinguish types
|
||||
- Filters let you focus on specific aspects
|
||||
- Local graphs show connections for individual documents
|
||||
|
||||
### Backlinks
|
||||
|
||||
Obsidian automatically tracks references between documents:
|
||||
|
||||
- View all documents that reference the current one
|
||||
- See the exact context of each reference
|
||||
- Navigate easily through connections
|
||||
- Track how concepts relate to each other
|
||||
|
||||
### Tag Explorer
|
||||
|
||||
Use tags to organize and filter content:
|
||||
|
||||
- View all tags in your knowledge base
|
||||
- See how many documents use each tag
|
||||
- Filter documents by tag combinations
|
||||
- Create hierarchical tag structures
|
||||
|
||||
## Knowledge Elements
|
||||
|
||||
Basic Memory's knowledge format works natively with Obsidian:
|
||||
|
||||
### Wiki Links
|
||||
|
||||
```markdown
|
||||
## Relations
|
||||
- implements [[Search Design]]
|
||||
- depends_on [[Database Schema]]
|
||||
```
|
||||
|
||||
These display as clickable links in Obsidian and appear in the graph view.
|
||||
|
||||
### Observations with Tags
|
||||
|
||||
```markdown
|
||||
## Observations
|
||||
- [tech] Using SQLite #database
|
||||
- [design] Local-first #architecture
|
||||
```
|
||||
|
||||
Tags become searchable and filterable in Obsidian's tag pane.
|
||||
|
||||
### Frontmatter
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Document Title
|
||||
type: note
|
||||
tags: [search, design]
|
||||
---
|
||||
```
|
||||
|
||||
Frontmatter provides metadata for Obsidian to use in search and filtering.
|
||||
|
||||
## Canvas Integration
|
||||
|
||||
Basic Memory can create [Obsidian Canvas](https://obsidian.md/canvas) files:
|
||||
|
||||
1. Ask Claude to create a visualization:
|
||||
```
|
||||
You: "Create a canvas showing the structure of our project components."
|
||||
```
|
||||
|
||||
2. Claude generates a .canvas file in your knowledge base
|
||||
|
||||
3. Open the file in Obsidian to view and edit the visual representation
|
||||
|
||||
4. Canvas files maintain references to your documents
|
||||
|
||||
## Recommended Plugins
|
||||
|
||||
These Obsidian plugins work especially well with Basic Memory:
|
||||
|
||||
- **Dataview**: Query your knowledge base programmatically
|
||||
- **Kanban**: Organize tasks from knowledge files
|
||||
- **Calendar**: View and navigate temporal knowledge
|
||||
- **Templates**: Create consistent knowledge structures
|
||||
|
||||
## Workflow Suggestions
|
||||
|
||||
### Daily Notes
|
||||
|
||||
```markdown
|
||||
# 2024-01-21
|
||||
|
||||
## Progress
|
||||
- Updated [[Search Design]]
|
||||
- Fixed [[Bug Report 123]]
|
||||
|
||||
## Notes
|
||||
- [idea] Better indexing #enhancement
|
||||
- [todo] Update docs #documentation
|
||||
|
||||
## Links
|
||||
- relates_to [[Current Sprint]]
|
||||
- updates [[Project Status]]
|
||||
```
|
||||
|
||||
### Project Tracking
|
||||
|
||||
```markdown
|
||||
# Current Sprint
|
||||
|
||||
## Tasks
|
||||
- [ ] Update [[Search]]
|
||||
- [ ] Fix [[Auth Bug]]
|
||||
|
||||
## Tags
|
||||
#sprint #planning #current
|
||||
```
|
||||
|
||||
## Relations
|
||||
- enhances [[Introduction to Basic Memory]] (Overview of system)
|
||||
- relates_to [[Canvas]] (Visual knowledge mapping)
|
||||
- complements [[User Guide]] (Using Basic Memory)
|
||||
@@ -1,311 +0,0 @@
|
||||
# Supabase OAuth Setup for Basic Memory
|
||||
|
||||
This guide explains how to set up Supabase as the OAuth provider for Basic Memory MCP server in production.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. A Supabase project (create one at [supabase.com](https://supabase.com))
|
||||
2. Basic Memory MCP server deployed
|
||||
3. Environment variables configuration
|
||||
|
||||
## Overview
|
||||
|
||||
The Supabase OAuth provider offers:
|
||||
- Production-ready authentication with persistent storage
|
||||
- User management through Supabase Auth
|
||||
- JWT token validation
|
||||
- Integration with Supabase's security features
|
||||
- Support for social logins (GitHub, Google, etc.)
|
||||
|
||||
## Setup Steps
|
||||
|
||||
### 1. Get Supabase Credentials
|
||||
|
||||
From your Supabase project dashboard:
|
||||
|
||||
1. Go to Settings > API
|
||||
2. Copy these values:
|
||||
- `Project URL` → `SUPABASE_URL`
|
||||
- `anon public` key → `SUPABASE_ANON_KEY`
|
||||
- `service_role` key → `SUPABASE_SERVICE_KEY` (keep this secret!)
|
||||
- JWT secret → `SUPABASE_JWT_SECRET` (under Settings > API > JWT Settings)
|
||||
|
||||
### 2. Configure Environment Variables
|
||||
|
||||
Create a `.env` file:
|
||||
|
||||
```bash
|
||||
# Enable OAuth
|
||||
FASTMCP_AUTH_ENABLED=true
|
||||
FASTMCP_AUTH_PROVIDER=supabase
|
||||
|
||||
# Your MCP server URL
|
||||
FASTMCP_AUTH_ISSUER_URL=https://your-mcp-server.com
|
||||
|
||||
# Supabase configuration
|
||||
SUPABASE_URL=https://your-project.supabase.co
|
||||
SUPABASE_ANON_KEY=your-anon-key
|
||||
SUPABASE_SERVICE_KEY=your-service-key
|
||||
SUPABASE_JWT_SECRET=your-jwt-secret
|
||||
|
||||
# Allowed OAuth clients (comma-separated)
|
||||
SUPABASE_ALLOWED_CLIENTS=web-app,mobile-app,cli-tool
|
||||
|
||||
# Required scopes
|
||||
FASTMCP_AUTH_REQUIRED_SCOPES=read,write
|
||||
```
|
||||
|
||||
### 3. Create OAuth Clients Table (Optional)
|
||||
|
||||
For production, create a table to store OAuth clients in Supabase:
|
||||
|
||||
```sql
|
||||
CREATE TABLE oauth_clients (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
client_id TEXT UNIQUE NOT NULL,
|
||||
client_secret TEXT NOT NULL,
|
||||
name TEXT,
|
||||
redirect_uris TEXT[],
|
||||
allowed_scopes TEXT[],
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create an index for faster lookups
|
||||
CREATE INDEX idx_oauth_clients_client_id ON oauth_clients(client_id);
|
||||
|
||||
-- RLS policies
|
||||
ALTER TABLE oauth_clients ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Only service role can manage clients
|
||||
CREATE POLICY "Service role can manage clients" ON oauth_clients
|
||||
FOR ALL USING (auth.jwt()->>'role' = 'service_role');
|
||||
```
|
||||
|
||||
### 4. Set Up Auth Flow
|
||||
|
||||
The Supabase OAuth provider handles the following flow:
|
||||
|
||||
1. **Client Authorization Request**
|
||||
```
|
||||
GET /authorize?client_id=web-app&redirect_uri=https://app.com/callback
|
||||
```
|
||||
|
||||
2. **Redirect to Supabase Auth**
|
||||
- User authenticates with Supabase (email/password, magic link, or social login)
|
||||
- Supabase redirects back to your MCP server
|
||||
|
||||
3. **Token Exchange**
|
||||
```
|
||||
POST /token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=authorization_code&code=xxx&client_id=web-app
|
||||
```
|
||||
|
||||
4. **Access Protected Resources**
|
||||
```
|
||||
GET /mcp
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
### 5. Enable Social Logins (Optional)
|
||||
|
||||
In Supabase dashboard:
|
||||
|
||||
1. Go to Authentication > Providers
|
||||
2. Enable desired providers (GitHub, Google, etc.)
|
||||
3. Configure OAuth apps for each provider
|
||||
4. Users can now log in via social providers
|
||||
|
||||
### 6. User Management
|
||||
|
||||
Supabase provides:
|
||||
- User registration and login
|
||||
- Password reset flows
|
||||
- Email verification
|
||||
- User metadata storage
|
||||
- Admin APIs for user management
|
||||
|
||||
Access user data in your MCP tools:
|
||||
|
||||
```python
|
||||
# In your MCP tool
|
||||
async def get_user_info(ctx: Context):
|
||||
# The token is already validated by the OAuth middleware
|
||||
user_id = ctx.auth.user_id
|
||||
email = ctx.auth.email
|
||||
|
||||
# Use Supabase client to get more user data if needed
|
||||
user = await supabase.auth.admin.get_user_by_id(user_id)
|
||||
return user
|
||||
```
|
||||
|
||||
### 7. Production Deployment
|
||||
|
||||
1. **Environment Security**
|
||||
- Never expose `SUPABASE_SERVICE_KEY`
|
||||
- Use environment variables, not hardcoded values
|
||||
- Rotate keys periodically
|
||||
|
||||
2. **HTTPS Required**
|
||||
- Always use HTTPS in production
|
||||
- Configure proper SSL certificates
|
||||
|
||||
3. **Rate Limiting**
|
||||
- Implement rate limiting for auth endpoints
|
||||
- Use Supabase's built-in rate limiting
|
||||
|
||||
4. **Monitoring**
|
||||
- Monitor auth logs in Supabase dashboard
|
||||
- Set up alerts for suspicious activity
|
||||
|
||||
## Testing
|
||||
|
||||
### Local Development
|
||||
|
||||
For local testing with Supabase:
|
||||
|
||||
```bash
|
||||
# Start MCP server with Supabase auth
|
||||
FASTMCP_AUTH_ENABLED=true \
|
||||
FASTMCP_AUTH_PROVIDER=supabase \
|
||||
SUPABASE_URL=http://localhost:54321 \
|
||||
SUPABASE_ANON_KEY=your-local-anon-key \
|
||||
bm mcp --transport streamable-http
|
||||
```
|
||||
|
||||
### Test Authentication Flow
|
||||
|
||||
```python
|
||||
import httpx
|
||||
import asyncio
|
||||
|
||||
async def test_supabase_auth():
|
||||
# 1. Register/login with Supabase directly
|
||||
supabase_url = "https://your-project.supabase.co"
|
||||
|
||||
# 2. Get MCP authorization URL
|
||||
response = await httpx.get(
|
||||
"http://localhost:8000/authorize",
|
||||
params={
|
||||
"client_id": "web-app",
|
||||
"redirect_uri": "http://localhost:3000/callback",
|
||||
"response_type": "code",
|
||||
}
|
||||
)
|
||||
|
||||
# 3. User logs in via Supabase
|
||||
# 4. Exchange code for MCP tokens
|
||||
# 5. Access protected resources
|
||||
|
||||
asyncio.run(test_supabase_auth())
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Custom User Metadata
|
||||
|
||||
Store additional user data in Supabase:
|
||||
|
||||
```sql
|
||||
-- Add custom fields to auth.users
|
||||
ALTER TABLE auth.users
|
||||
ADD COLUMN IF NOT EXISTS metadata JSONB DEFAULT '{}';
|
||||
|
||||
-- Or create a separate profiles table
|
||||
CREATE TABLE profiles (
|
||||
id UUID REFERENCES auth.users PRIMARY KEY,
|
||||
username TEXT UNIQUE,
|
||||
avatar_url TEXT,
|
||||
bio TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### Row Level Security (RLS)
|
||||
|
||||
Protect user data with RLS:
|
||||
|
||||
```sql
|
||||
-- Users can only access their own data
|
||||
CREATE POLICY "Users can view own profile" ON profiles
|
||||
FOR SELECT USING (auth.uid() = id);
|
||||
|
||||
CREATE POLICY "Users can update own profile" ON profiles
|
||||
FOR UPDATE USING (auth.uid() = id);
|
||||
```
|
||||
|
||||
### Custom Claims
|
||||
|
||||
Add custom claims to JWT tokens:
|
||||
|
||||
```sql
|
||||
-- Function to add custom claims
|
||||
CREATE OR REPLACE FUNCTION custom_jwt_claims()
|
||||
RETURNS JSON AS $$
|
||||
BEGIN
|
||||
RETURN json_build_object(
|
||||
'user_role', current_setting('request.jwt.claims')::json->>'user_role',
|
||||
'permissions', current_setting('request.jwt.claims')::json->>'permissions'
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Invalid JWT Secret**
|
||||
- Ensure `SUPABASE_JWT_SECRET` matches your Supabase project
|
||||
- Check Settings > API > JWT Settings in Supabase dashboard
|
||||
|
||||
2. **CORS Errors**
|
||||
- Configure CORS in your MCP server
|
||||
- Add allowed origins in Supabase dashboard
|
||||
|
||||
3. **Token Validation Fails**
|
||||
- Verify tokens are being passed correctly
|
||||
- Check token expiration times
|
||||
- Ensure scopes match requirements
|
||||
|
||||
4. **User Not Found**
|
||||
- Confirm user exists in Supabase Auth
|
||||
- Check if email is verified (if required)
|
||||
- Verify client permissions
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug logging:
|
||||
|
||||
```bash
|
||||
export FASTMCP_LOG_LEVEL=DEBUG
|
||||
export SUPABASE_LOG_LEVEL=debug
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Secure Keys**: Never commit secrets to version control
|
||||
2. **Least Privilege**: Use minimal required scopes
|
||||
3. **Token Rotation**: Implement refresh token rotation
|
||||
4. **Audit Logs**: Monitor authentication events
|
||||
5. **Rate Limiting**: Protect against brute force attacks
|
||||
6. **HTTPS Only**: Always use encrypted connections
|
||||
|
||||
## Migration from Basic Auth
|
||||
|
||||
To migrate from the basic auth provider:
|
||||
|
||||
1. Export existing user data
|
||||
2. Import users into Supabase Auth
|
||||
3. Update client applications to use new auth flow
|
||||
4. Gradually transition users to Supabase login
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Set up email templates in Supabase
|
||||
- Configure password policies
|
||||
- Implement MFA (multi-factor authentication)
|
||||
- Add social login providers
|
||||
- Create admin dashboard for user management
|
||||
@@ -1,243 +0,0 @@
|
||||
---
|
||||
title: Technical Information
|
||||
type: note
|
||||
permalink: docs/technical-information
|
||||
---
|
||||
|
||||
# Technical Information
|
||||
|
||||
This document provides technical details about Basic Memory's implementation, licensing, and integration with the Model Context Protocol (MCP).
|
||||
|
||||
## Architecture
|
||||
|
||||
Basic Memory consists of:
|
||||
|
||||
1. **Core Knowledge Engine**: Parses and indexes Markdown files
|
||||
2. **SQLite Database**: Provides fast querying and search
|
||||
3. **MCP Server**: Implements the Model Context Protocol
|
||||
4. **CLI Tools**: Command-line utilities for management
|
||||
5. **Sync Service**: Monitors file changes and updates the database
|
||||
|
||||
The system follows a file-first architecture where all knowledge is represented in standard Markdown files and the database serves as a secondary index.
|
||||
|
||||
## Model Context Protocol (MCP)
|
||||
|
||||
Basic Memory implements the [Model Context Protocol](https://github.com/modelcontextprotocol/spec), an open standard for enabling AI models to access external tools:
|
||||
|
||||
- **Standardized Interface**: Common protocol for tool integration
|
||||
- **Tool Registration**: Basic Memory registers as a tool provider
|
||||
- **Asynchronous Communication**: Enables efficient interaction with AI models
|
||||
- **Standardized Schema**: Structured data exchange format
|
||||
|
||||
Integration with Claude Desktop uses the MCP to grant Claude access to your knowledge base through a set of specialized tools that search, read, and write knowledge.
|
||||
|
||||
## Licensing
|
||||
|
||||
Basic Memory is licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.en.html):
|
||||
|
||||
- **Free Software**: You can use, study, share, and modify the software
|
||||
- **Copyleft**: Derivative works must be distributed under the same license
|
||||
- **Network Use**: Network users must be able to receive the source code
|
||||
- **Commercial Use**: Allowed, subject to license requirements
|
||||
|
||||
The AGPL license ensures Basic Memory remains open source while protecting against proprietary forks.
|
||||
|
||||
## Source Code
|
||||
|
||||
Basic Memory is developed as an open-source project:
|
||||
|
||||
- **GitHub Repository**: [https://github.com/basicmachines-co/basic-memory](https://github.com/basicmachines-co/basic-memory)
|
||||
- **Issue Tracker**: Report bugs and request features on GitHub
|
||||
- **Contributions**: Pull requests are welcome following the contributing guidelines
|
||||
- **Documentation**: Source for this documentation is also available in the repository
|
||||
|
||||
## Data Storage and Privacy
|
||||
|
||||
Basic Memory is designed with privacy as a core principle:
|
||||
|
||||
- **Local-First**: All data remains on your local machine
|
||||
- **No Cloud Dependency**: No remote servers or accounts required
|
||||
- **Telemetry**: Optional and disabled by default
|
||||
- **Standard Formats**: All data is stored in standard file formats you control
|
||||
|
||||
## Implementation Details
|
||||
|
||||
Knowledge in Basic Memory is organized as a semantic graph:
|
||||
|
||||
1. **Entities** - Distinct concepts represented by Markdown documents
|
||||
2. **Observations** - Categorized facts and information about entities
|
||||
3. **Relations** - Connections between entities that form the knowledge graph
|
||||
|
||||
This structure emerges from simple text patterns in standard Markdown:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
type: note
|
||||
permalink: coffee/coffee-brewing-methods
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#brewing'
|
||||
- '#methods'
|
||||
- '#demo'
|
||||
---
|
||||
|
||||
# Coffee Brewing Methods
|
||||
|
||||
An exploration of different coffee brewing techniques, their characteristics, and how they affect flavor extraction.
|
||||
|
||||
## Overview
|
||||
|
||||
Coffee brewing is both an art and a science. Different brewing methods extract different compounds from coffee beans,
|
||||
resulting in unique flavor profiles, body, and mouthfeel. The key variables in any brewing method are:
|
||||
|
||||
- Grind size
|
||||
- Water temperature
|
||||
- Brew time
|
||||
- Coffee-to-water ratio
|
||||
- Agitation/turbulence
|
||||
|
||||
## Observations
|
||||
|
||||
- [principle] Coffee extraction follows a predictable pattern: acids extract first, then sugars, then bitter compounds
|
||||
#extraction
|
||||
- [method] Pour over methods generally produce cleaner, brighter cups with more distinct flavor notes #clarity
|
||||
|
||||
## Relations
|
||||
|
||||
- requires [[Proper Grinding Technique]]
|
||||
- affects [[Flavor Extraction]]
|
||||
```
|
||||
|
||||
Becomes
|
||||
|
||||
```json
|
||||
{
|
||||
"entities": [
|
||||
{
|
||||
"permalink": "coffee/coffee-brewing-methods",
|
||||
"title": "Coffee Brewing Methods",
|
||||
"file_path": "Coffee Notes/Coffee Brewing Methods.md",
|
||||
"entity_type": "note",
|
||||
"entity_metadata": {
|
||||
"title": "Coffee Brewing Methods",
|
||||
"type": "note",
|
||||
"permalink": "coffee/coffee-brewing-methods",
|
||||
"tags": "['#coffee', '#brewing', '#methods', '#demo']"
|
||||
},
|
||||
"checksum": "bfa32a0f23fa124b53f0694c344d2788b0ce50bd090b55b6d738401d2a349e4c",
|
||||
"content_type": "text/markdown",
|
||||
"observations": [
|
||||
{
|
||||
"category": "principle",
|
||||
"content": "Coffee extraction follows a predictable pattern: acids extract first, then sugars, then bitter compounds #extraction",
|
||||
"tags": [
|
||||
"extraction"
|
||||
],
|
||||
"permalink": "coffee/coffee-brewing-methods/observations/principle/coffee-extraction-follows-a-predictable-pattern-acids-extract-first-then-sugars-then-bitter-compounds-extraction"
|
||||
},
|
||||
{
|
||||
"category": "method",
|
||||
"content": "Pour over methods generally produce cleaner, brighter cups with more distinct flavor notes #clarity",
|
||||
"tags": [
|
||||
"clarity"
|
||||
],
|
||||
"permalink": "coffee/coffee-brewing-methods/observations/method/pour-over-methods-generally-produce-cleaner-brighter-cups-with-more-distinct-flavor-notes-clarity"
|
||||
}
|
||||
],
|
||||
"relations": [
|
||||
{
|
||||
"from_id": "coffee/coffee-bean-origins",
|
||||
"to_id": "coffee/coffee-brewing-methods",
|
||||
"relation_type": "pairs_with",
|
||||
"permalink": "coffee/coffee-bean-origins/pairs-with/coffee/coffee-brewing-methods",
|
||||
"to_name": "Coffee Brewing Methods"
|
||||
},
|
||||
{
|
||||
"from_id": "coffee/flavor-extraction",
|
||||
"to_id": "coffee/coffee-brewing-methods",
|
||||
"relation_type": "affected_by",
|
||||
"permalink": "coffee/flavor-extraction/affected-by/coffee/coffee-brewing-methods",
|
||||
"to_name": "Coffee Brewing Methods"
|
||||
}
|
||||
],
|
||||
"created_at": "2025-03-06T14:01:23.445071",
|
||||
"updated_at": "2025-03-06T13:34:48.563606"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Basic Memory understands how to build context via its semantic graph.
|
||||
|
||||
### Entity Model
|
||||
|
||||
Basic Memory's core data model consists of:
|
||||
|
||||
- **Entities**: Documents in your knowledge base
|
||||
- **Observations**: Facts or statements about entities
|
||||
- **Relations**: Connections between entities
|
||||
- **Tags**: Additional categorization for entities and observations
|
||||
|
||||
The system parses Markdown files to extract this structured information while preserving the human-readable format.
|
||||
|
||||
### Files as Source of Truth
|
||||
|
||||
Plain Markdown files store all knowledge, making it accessible with any text editor and easy to version with git.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
User((User)) <--> |Conversation| Claude["Claude or other LLM"]
|
||||
Claude <-->|API Calls| BMCP["Basic Memory MCP Server"]
|
||||
|
||||
subgraph "Local Storage"
|
||||
KnowledgeFiles["Markdown Files - Source of Truth"]
|
||||
KnowledgeIndex[(Knowledge Graph SQLite Index)]
|
||||
end
|
||||
|
||||
BMCP <-->|"write_note() read_note()"| KnowledgeFiles
|
||||
BMCP <-->|"search_notes() build_context()"| KnowledgeIndex
|
||||
KnowledgeFiles <-.->|Sync Process| KnowledgeIndex
|
||||
KnowledgeFiles <-->|Direct Editing| Editors((Text Editors & Git))
|
||||
|
||||
User -.->|"Complete control, Privacy preserved"| KnowledgeFiles
|
||||
|
||||
|
||||
class Claude primary
|
||||
class BMCP secondary
|
||||
class KnowledgeFiles tertiary
|
||||
class KnowledgeIndex quaternary
|
||||
class User,Editors user`;
|
||||
```
|
||||
|
||||
### Sqlite Database
|
||||
|
||||
A local SQLite database maintains the knowledge graph topology for fast queries and semantic traversal without cloud dependencies. It contains:
|
||||
- db tables for the knowledge graph schema
|
||||
- a search index table enabling full text search across the knowledge base
|
||||
|
||||
|
||||
### Sync Process
|
||||
|
||||
The sync process:
|
||||
|
||||
1. Detects changes to files in the knowledge directory
|
||||
2. Parses modified files to extract structured data
|
||||
3. Updates the SQLite database with changes
|
||||
4. Resolves forward references when new entities are created
|
||||
5. Updates the search index for fast querying
|
||||
|
||||
### Search Engine
|
||||
|
||||
The search functionality:
|
||||
|
||||
1. Uses a combination of full-text search and semantic matching
|
||||
2. Indexes observations, relations, and content
|
||||
3. Supports wildcards and pattern matching in memory:// URLs
|
||||
4. Traverses the knowledge graph to follow relationships
|
||||
5. Ranks results by relevance to the query
|
||||
|
||||
## Relations
|
||||
- relates_to [[Welcome to Basic memory]] (Overview)
|
||||
- relates_to [[CLI Reference]] (Command line tools)
|
||||
- implements [[Knowledge Format]] (File structure and format)
|
||||
@@ -1,657 +0,0 @@
|
||||
---
|
||||
title: User Guide
|
||||
type: note
|
||||
permalink: docs/user-guide
|
||||
---
|
||||
|
||||
# User Guide
|
||||
|
||||
This guide explains how to effectively use Basic Memory in your daily workflow, from creating knowledge through
|
||||
conversations to building a rich semantic network.
|
||||
|
||||
## Basic Memory Workflow
|
||||
|
||||
Using Basic Memory follows a natural cycle:
|
||||
|
||||
1. **Have conversations** with AI assistants like Claude
|
||||
2. **Capture knowledge** in Markdown files
|
||||
3. **Build connections** between pieces of knowledge
|
||||
4. **Reference your knowledge** in future conversations
|
||||
5. **Edit files directly** when needed
|
||||
6. **Sync changes** automatically
|
||||
|
||||
## Creating Knowledge
|
||||
|
||||
### Through Conversations
|
||||
|
||||
To create knowledge during conversations with Claude:
|
||||
|
||||
```
|
||||
You: We've covered several authentication approaches. Could you create a note summarizing what we've discussed?
|
||||
|
||||
Claude: I'll create a note summarizing our authentication discussion.
|
||||
```
|
||||
|
||||
This creates a Markdown file in your `~/basic-memory` directory with semantic markup.
|
||||
|
||||
### Direct File Creation
|
||||
|
||||
You can create files directly:
|
||||
|
||||
1. Create a new Markdown file in your `~/basic-memory` directory
|
||||
2. Add frontmatter with title, type, and optional tags
|
||||
3. Structure content with observations and relations
|
||||
4. Save the file
|
||||
5. Run `basic-memory sync` if not in watch mode
|
||||
|
||||
## Using Special Prompts
|
||||
|
||||
Basic Memory includes several special prompts that help you leverage your knowledge base more effectively. In apps like
|
||||
Claude Desktop, these prompts trigger specific tools to search and analyze your knowledge base.
|
||||
|
||||
### Continue Conversation
|
||||
|
||||
When you want to pick up where you left off on a topic:
|
||||
|
||||
```
|
||||
You: Let's continue our conversation about authentication systems.
|
||||
```
|
||||
|
||||
Behind the scenes:
|
||||
|
||||
- Claude searches your knowledge base for content about "authentication systems"
|
||||
- It retrieves relevant documents and their relations
|
||||
- It analyzes the context to understand where you left off
|
||||
- It builds a comprehensive picture of what you've previously discussed
|
||||
- It can then resume the conversation with all that context
|
||||
|
||||
This is particularly useful when:
|
||||
|
||||
- Starting a new session days or weeks after your last discussion
|
||||
- Switching between multiple ongoing projects
|
||||
- Building on previous work without repeating yourself
|
||||
|
||||
### Recent Activity
|
||||
|
||||
To get an overview of what you've been working on:
|
||||
|
||||
```
|
||||
You: What have we been discussing recently?
|
||||
```
|
||||
|
||||
Behind the scenes:
|
||||
|
||||
- Claude retrieves documents modified recently
|
||||
- It analyzes patterns and themes
|
||||
- It summarizes the key topics and changes
|
||||
- It offers to continue working on any of those topics
|
||||
|
||||
This is useful for:
|
||||
|
||||
- Coming back after a break
|
||||
- Getting a quick reminder of ongoing projects
|
||||
- Deciding what to work on next
|
||||
|
||||
### Search
|
||||
|
||||
To find specific information in your knowledge base:
|
||||
|
||||
```
|
||||
You: Find information about JWT authentication in my notes.
|
||||
```
|
||||
|
||||
Behind the scenes:
|
||||
|
||||
- Claude performs a semantic search for "JWT authentication"
|
||||
- It retrieves and ranks the most relevant documents
|
||||
- It summarizes the key findings
|
||||
- It offers to explore specific areas in more detail
|
||||
|
||||
This is useful for:
|
||||
|
||||
- Finding specific information quickly
|
||||
- Exploring what you know about a topic
|
||||
- Starting work on an existing topic
|
||||
|
||||
### Example
|
||||
|
||||
Choose "Continue Conversation"
|
||||
![[prompt 1.png|500]]
|
||||
|
||||
Enter a topic
|
||||
![[prompt2.png|500]]
|
||||
|
||||
Give instructions
|
||||
![[prompt3.png|500]]
|
||||
|
||||
Claude Desktop lets you send a prompt to provide context. You can use this at the beginning of a chat to preload context
|
||||
without needing to copy paste all the time. By using one of the supplied prompts, Basic Memory will search the knowledge
|
||||
base and give the AI instructions for how to build context.
|
||||
|
||||
Choose "Continue Conversation":
|
||||
|
||||
![[prompt 1.png|500]]
|
||||
|
||||
Enter a topic:
|
||||
|
||||
![[prompt2.png|500]]
|
||||
|
||||
Give optional additional instructions:
|
||||
|
||||
![[prompt3.png|500]]
|
||||
|
||||
Claude can build context from the supplied topic. This works independently of Claude Project information. All the
|
||||
context comes from your local knowledge base.
|
||||
|
||||
![[prompt4.png|500]]
|
||||
|
||||
## Searching Your Knowledge Base
|
||||
|
||||
Basic Memory provides multiple ways to search and explore your knowledge base:
|
||||
|
||||
### Natural Language Search
|
||||
|
||||
The simplest way to search is to ask Claude directly:
|
||||
|
||||
```
|
||||
You: What do I know about authentication methods?
|
||||
```
|
||||
|
||||
Claude will search your knowledge base semantically and return relevant information.
|
||||
|
||||
### Search Prompt
|
||||
|
||||
Use the dedicated search prompt for more focused searches:
|
||||
|
||||
```
|
||||
You: Search for "JWT authentication"
|
||||
```
|
||||
|
||||
This triggers a specialized search that returns precise results with document titles, relevant excerpts, and offers to
|
||||
explore specific documents.
|
||||
|
||||
### Boolean Search
|
||||
|
||||
For more precise searches, use boolean operators to refine your queries:
|
||||
|
||||
```
|
||||
You: Search for "authentication AND OAuth NOT basic"
|
||||
```
|
||||
|
||||
Basic Memory supports standard boolean operators:
|
||||
|
||||
- **AND**: Find documents containing both terms
|
||||
```
|
||||
You: Search for "python AND flask"
|
||||
```
|
||||
This finds documents containing both "python" and "flask"
|
||||
|
||||
- **OR**: Find documents containing either term
|
||||
```
|
||||
You: Search for "python OR javascript"
|
||||
```
|
||||
This finds documents containing either "python" or "javascript"
|
||||
|
||||
- **NOT**: Exclude documents containing specific terms
|
||||
```
|
||||
You: Search for "python NOT django"
|
||||
```
|
||||
This finds documents containing "python" but excludes those containing "django"
|
||||
|
||||
- **Grouping with parentheses**: Control operator precedence
|
||||
```
|
||||
You: Search for "(python OR javascript) AND web"
|
||||
```
|
||||
This finds documents about web development that mention either Python or JavaScript
|
||||
|
||||
Boolean search is particularly useful for:
|
||||
|
||||
- Narrowing down results in large knowledge bases
|
||||
- Finding specific combinations of concepts
|
||||
- Excluding irrelevant content from search results
|
||||
- Creating complex queries for precise information retrieval
|
||||
|
||||
### Memory URL Pattern Matching
|
||||
|
||||
For advanced searches, use memory:// URL patterns with wildcards:
|
||||
|
||||
```
|
||||
You: Look at memory://auth* and summarize all authentication approaches.
|
||||
```
|
||||
|
||||
Pattern matching supports:
|
||||
|
||||
- **Wildcards**: `memory://auth*` matches all permalinks starting with "auth"
|
||||
- **Path patterns**: `memory://project/*/auth` matches auth documents in any project subfolder
|
||||
- **Relation traversal**: `memory://auth-system/implements/*` finds all documents that implement the auth system
|
||||
|
||||
### Combining Search with Context Building
|
||||
|
||||
The most powerful searches build comprehensive context by following relationships:
|
||||
|
||||
```
|
||||
You: Search for JWT authentication and then follow all implementation relations.
|
||||
```
|
||||
|
||||
This builds a complete picture by:
|
||||
|
||||
1. Finding documents about JWT authentication
|
||||
2. Following implementation relationships from those documents
|
||||
3. Building a complete picture of how JWT is implemented across your system
|
||||
|
||||
### Search Best Practices
|
||||
|
||||
For effective searching:
|
||||
|
||||
1. **Be specific** with search terms and phrases
|
||||
2. **Use boolean operators** to refine searches and find precise information
|
||||
3. **Use technical terms** when searching for technical content
|
||||
4. **Follow up** on search results by asking for more details about specific documents
|
||||
5. **Combine approaches** by starting with search and then using memory:// URLs for precision
|
||||
6. **Use relation traversal** to explore connected concepts after finding initial documents
|
||||
|
||||
## Referencing Knowledge
|
||||
|
||||
### Using memory:// URLs
|
||||
|
||||
Reference specific knowledge directly:
|
||||
|
||||
```
|
||||
You: Please look at memory://authentication-approaches and suggest which approach would be best for our mobile app.
|
||||
```
|
||||
|
||||
### Natural Language References
|
||||
|
||||
Reference knowledge conversationally:
|
||||
|
||||
```
|
||||
You: What did we decide about authentication for the project?
|
||||
```
|
||||
|
||||
### Advanced References
|
||||
|
||||
Follow connections across your knowledge graph:
|
||||
|
||||
```
|
||||
You: Look at memory://project-architecture and check related documents to give me a complete picture.
|
||||
```
|
||||
|
||||
## Working with Files
|
||||
|
||||
### File Location and Organization
|
||||
|
||||
By default, Basic Memory stores files in `~/basic-memory`:
|
||||
|
||||
- Browse this directory in your file explorer
|
||||
- Organize files into subfolders
|
||||
- Use git for version control
|
||||
|
||||
### File Format
|
||||
|
||||
Each knowledge file follows this structure:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Authentication Approaches
|
||||
type: note
|
||||
tags: [security, architecture]
|
||||
permalink: authentication-approaches
|
||||
---
|
||||
|
||||
# Authentication Approaches
|
||||
|
||||
A comparison of authentication methods.
|
||||
|
||||
## Observations
|
||||
|
||||
- [approach] JWT provides stateless authentication #security
|
||||
- [limitation] Session tokens require server-side storage #infrastructure
|
||||
|
||||
## Relations
|
||||
|
||||
- implements [[Security Requirements]]
|
||||
- affects [[User Login Flow]]
|
||||
```
|
||||
|
||||
### Editing Files
|
||||
|
||||
Modify files in any text editor:
|
||||
|
||||
1. Open the file in your preferred editor
|
||||
2. Make changes to content, observations, or relations
|
||||
3. Save the file
|
||||
4. Basic Memory detects changes automatically when running in watch mode
|
||||
|
||||
## Building a Knowledge Graph
|
||||
|
||||
The value of Basic Memory comes from connections between pieces of knowledge.
|
||||
|
||||
### Creating Relations
|
||||
|
||||
When creating or editing notes, build connections:
|
||||
|
||||
```markdown
|
||||
## Relations
|
||||
|
||||
- implements [[Security Requirements]]
|
||||
- depends_on [[User Authentication]]
|
||||
```
|
||||
|
||||
Relations can be:
|
||||
|
||||
- Hierarchical (part_of, contains)
|
||||
- Directional (implements, depends_on)
|
||||
- Associative (relates_to, similar_to)
|
||||
- Temporal (precedes, follows)
|
||||
|
||||
Relations are also created via regular wiki-link style links within the body text.
|
||||
|
||||
### Forward References
|
||||
|
||||
Reference documents that don't exist yet:
|
||||
|
||||
```markdown
|
||||
- will_impact [[Future Feature]]
|
||||
```
|
||||
|
||||
These references resolve automatically when you create the referenced document.
|
||||
|
||||
## Conversation Continuity
|
||||
|
||||
Basic Memory maintains context across different conversations.
|
||||
|
||||
### Starting New Sessions with Context
|
||||
|
||||
When starting a new conversation with Claude, you can:
|
||||
|
||||
1. **Use special prompts** like "Continue conversation about..." or "What were we working on?"
|
||||
2. **Reference specific documents** with memory:// URLs
|
||||
3. **Ask about recent work** with "What have we been discussing recently?"
|
||||
4. **Search for specific topics** with "Find information about..."
|
||||
|
||||
### Long-Term Projects
|
||||
|
||||
Maintain context for complex projects over time:
|
||||
|
||||
1. **Document key decisions** as you make them
|
||||
2. **Create relationships** between project components
|
||||
3. **Reference past decisions** when implementing features
|
||||
4. **Update documentation** as the project evolves
|
||||
|
||||
### Tips for Effective Continuity
|
||||
|
||||
1. **Be specific about topics** when continuing a conversation
|
||||
2. **Reference documents directly** with memory:// URLs for precision
|
||||
3. **Create summary notes** after important discussions
|
||||
4. **Update existing notes** rather than creating duplicates
|
||||
5. **Build robust connections** between related topics
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Note Editing (New in v0.13.0)
|
||||
|
||||
**Edit notes incrementally without rewriting entire documents:**
|
||||
|
||||
```
|
||||
💬 "Add a new section about deployment to my API documentation"
|
||||
🤖 [Uses edit_note to append new section]
|
||||
|
||||
💬 "Update the date at the top of my meeting notes"
|
||||
🤖 [Uses edit_note to prepend new timestamp]
|
||||
|
||||
💬 "Replace the implementation section in my design doc"
|
||||
🤖 [Uses edit_note to replace specific section]
|
||||
```
|
||||
|
||||
Available editing operations:
|
||||
- **Append**: Add content to end of notes
|
||||
- **Prepend**: Add content to beginning of notes
|
||||
- **Replace Section**: Replace content under specific headers
|
||||
- **Find & Replace**: Simple text replacements with validation
|
||||
|
||||
### File Management (New in v0.13.0)
|
||||
|
||||
**Move and organize notes with full database consistency:**
|
||||
|
||||
```
|
||||
💬 "Move my old meeting notes to the archive folder"
|
||||
🤖 [Uses move_note with automatic folder creation and database updates]
|
||||
|
||||
💬 "Reorganize my project files into a better structure"
|
||||
🤖 [Moves files while maintaining search indexes and links]
|
||||
```
|
||||
|
||||
Move operations include:
|
||||
- **Database Consistency**: Updates file paths, permalinks, and checksums
|
||||
- **Search Reindexing**: Maintains search functionality after moves
|
||||
- **Folder Creation**: Automatically creates destination directories
|
||||
- **Project Isolation**: Moves are contained within the current project
|
||||
- **Rollback Protection**: Ensures data integrity during failed operations
|
||||
|
||||
### Enhanced Search (New in v0.13.0)
|
||||
|
||||
**Frontmatter tags are now searchable:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
tags: [coffee, brewing, equipment]
|
||||
---
|
||||
```
|
||||
|
||||
Now searchable by: "coffee", "brewing", "equipment", or "Coffee Brewing Methods"
|
||||
|
||||
### Importing External Knowledge
|
||||
|
||||
Import existing conversations:
|
||||
|
||||
```bash
|
||||
# From Claude
|
||||
basic-memory import claude conversations
|
||||
|
||||
# From ChatGPT
|
||||
basic-memory import chatgpt
|
||||
|
||||
# Target specific projects (v0.13.0)
|
||||
basic-memory --project=work import claude conversations
|
||||
```
|
||||
|
||||
After importing, changes sync automatically in real-time.
|
||||
|
||||
### Obsidian Integration
|
||||
|
||||
Use with [Obsidian](https://obsidian.md):
|
||||
|
||||
1. Point Obsidian to your `~/basic-memory` directory
|
||||
2. Use Obsidian's graph view to visualize your knowledge network
|
||||
3. All changes sync back to Basic Memory
|
||||
|
||||
### Canvas Visualizations
|
||||
|
||||
Create visual knowledge maps:
|
||||
|
||||
```
|
||||
You: Could you create a canvas visualization of our project components?
|
||||
```
|
||||
|
||||
This generates an Obsidian canvas file showing the relationships between concepts.
|
||||
|
||||
### Advanced Memory URI Patterns
|
||||
|
||||
Use wildcards and patterns:
|
||||
|
||||
```
|
||||
You: Review memory://project/*/requirements to summarize all project requirements.
|
||||
```
|
||||
|
||||
## Command Line Interface
|
||||
|
||||
### Sync Commands
|
||||
|
||||
```bash
|
||||
# One-time sync
|
||||
basic-memory sync
|
||||
|
||||
# Watch for changes
|
||||
basic-memory sync --watch
|
||||
```
|
||||
|
||||
### Status and Information
|
||||
|
||||
```bash
|
||||
# Check system status
|
||||
basic-memory status
|
||||
|
||||
# View CLI help
|
||||
basic-memory --help
|
||||
```
|
||||
|
||||
### Import Commands
|
||||
|
||||
```bash
|
||||
# Import from Claude
|
||||
basic-memory import claude conversations
|
||||
|
||||
# Import from ChatGPT
|
||||
basic-memory import chatgpt
|
||||
```
|
||||
|
||||
## Multiple Projects (v0.13.0)
|
||||
|
||||
Basic Memory v0.13.0 introduces **fluid project management** - the ability to switch between projects instantly during conversations without restart. This allows you to maintain separate knowledge graphs for different purposes while seamlessly switching between them.
|
||||
|
||||
### Instant Project Switching (New in v0.13.0)
|
||||
|
||||
**Switch projects during conversations:**
|
||||
|
||||
```
|
||||
💬 "What projects do I have?"
|
||||
🤖 Available projects:
|
||||
• main (current, default)
|
||||
• work-notes
|
||||
• personal-journal
|
||||
• code-snippets
|
||||
|
||||
💬 "Switch to work-notes"
|
||||
🤖 ✓ Switched to work-notes project
|
||||
|
||||
Project Summary:
|
||||
• 47 entities
|
||||
• 125 observations
|
||||
• 23 relations
|
||||
|
||||
💬 "What did I work on yesterday?"
|
||||
🤖 [Shows recent activity from work-notes project]
|
||||
```
|
||||
|
||||
### Project-Specific Operations (New in v0.13.0)
|
||||
|
||||
Some MCP tools support optional project parameters for targeting specific projects:
|
||||
|
||||
```
|
||||
💬 "Create a note about this meeting in my personal-notes project"
|
||||
🤖 [Creates note in personal-notes project]
|
||||
|
||||
💬 "Switch to my work project"
|
||||
🤖 [Switches project context, then all operations work within that project]
|
||||
```
|
||||
|
||||
**Note**: Operations like search, move, and edit work within the currently active project. To work with content in different projects, switch to that project first or use the project parameter where supported.
|
||||
|
||||
### Managing Projects
|
||||
|
||||
```bash
|
||||
# List all configured projects
|
||||
basic-memory project list
|
||||
|
||||
# Add a new project
|
||||
basic-memory project create work ~/work-basic-memory
|
||||
|
||||
# Set the default project
|
||||
basic-memory project set-default work
|
||||
|
||||
# Remove a project (doesn't delete files)
|
||||
basic-memory project delete personal
|
||||
|
||||
# Show current project statistics
|
||||
basic-memory project info
|
||||
```
|
||||
|
||||
### Using Projects in Commands
|
||||
|
||||
All commands support the `--project` flag to specify which project to use:
|
||||
|
||||
```bash
|
||||
# Sync a specific project
|
||||
basic-memory --project=work sync
|
||||
|
||||
# Run MCP server for a specific project
|
||||
basic-memory --project=personal mcp
|
||||
```
|
||||
|
||||
You can also set the `BASIC_MEMORY_PROJECT` environment variable:
|
||||
|
||||
```bash
|
||||
BASIC_MEMORY_PROJECT=work basic-memory sync
|
||||
```
|
||||
|
||||
### Unified Database Architecture (New in v0.13.0)
|
||||
|
||||
Basic Memory v0.13.0 uses a unified database architecture:
|
||||
|
||||
- **Single Database**: All projects share `~/.basic-memory/memory.db`
|
||||
- **Project Isolation**: Proper data separation with project context
|
||||
- **Better Performance**: Optimized queries and reduced file I/O
|
||||
- **Easier Backup**: Single database file contains all project data
|
||||
- **Session Context**: Maintains active project throughout conversations
|
||||
|
||||
## Workflow Tips
|
||||
|
||||
### General Workflow
|
||||
1. **Project Organization**: Use multiple projects to separate different areas (work, personal, research)
|
||||
2. **Session Context**: Switch projects during conversations without restart (v0.13.0)
|
||||
3. **Real-time Sync**: Changes sync automatically - no need to run watch mode
|
||||
4. **Review Content**: Edit AI-created content for accuracy
|
||||
5. **Build Connections**: Create rich relationships between related ideas
|
||||
6. **Use Special Prompts**: Start conversations with context from your knowledge base
|
||||
|
||||
### v0.13.0 Workflow Enhancements
|
||||
7. **Incremental Editing**: Use edit_note for small changes instead of rewriting entire documents
|
||||
8. **File Organization**: Move and reorganize notes as your knowledge base grows
|
||||
9. **Project-Specific Creation**: Create notes in specific projects using project parameters
|
||||
10. **Search Tags**: Use frontmatter tags to improve content discoverability
|
||||
11. **Project Statistics**: Monitor project growth and activity with project info commands
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Sync Issues
|
||||
|
||||
If changes aren't showing up:
|
||||
|
||||
1. Run `basic-memory status` to check system state
|
||||
2. Try a manual sync with `basic-memory sync`
|
||||
|
||||
### Missing Content
|
||||
|
||||
If content isn't found:
|
||||
|
||||
1. Check the exact path and permalink
|
||||
2. Try searching with more general terms
|
||||
3. Verify the file exists in your knowledge base
|
||||
|
||||
### Relation Problems
|
||||
|
||||
If relations aren't working:
|
||||
|
||||
1. Ensure exact title matching in [[WikiLinks]]
|
||||
2. Check for typos in relation types
|
||||
3. Verify both documents exist
|
||||
|
||||
## Relations
|
||||
|
||||
- implements [[Knowledge Format]] (How knowledge is structured)
|
||||
- relates_to [[Getting Started with Basic Memory]] (Setup and first steps)
|
||||
- relates_to [[Canvas]] (Creating visual knowledge maps)
|
||||
- relates_to [[CLI Reference]] (Command line tools)
|
||||
- enhanced_in_v0.13.0 [[OAuth Authentication Guide]] (Production authentication)
|
||||
- enhanced_in_v0.13.0 [[Project Management]] (Multi-project workflows)
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
title: Introduction to Basic Memory
|
||||
type: docs
|
||||
permalink: docs/introduction
|
||||
tags:
|
||||
- documentation
|
||||
- index
|
||||
- overview
|
||||
---
|
||||
|
||||
# BASIC MEMORY
|
||||
|
||||
Basic Memory is a knowledge management system that allows you to build a persistent semantic graph from conversations
|
||||
with AI assistants. All knowledge is stored in standard Markdown files on your computer, giving you full control and
|
||||
ownership of your data.
|
||||
|
||||
Basic Memory connects you and AI assistants through shared knowledge:
|
||||
|
||||
1. **Captures knowledge** from natural conversations with AI assistants
|
||||
2. **Structures information** using simple semantic patterns in Markdown
|
||||
3. **Enables knowledge reuse** across different conversations and sessions
|
||||
4. **Maintains persistence** through local files you control completely
|
||||
|
||||
Both you and AI assistants like Claude can read from and write to the same knowledge base, creating a continuous
|
||||
learning environment where each conversation builds upon previous ones.
|
||||
|
||||
## Pick up your conversation right where you left off
|
||||
|
||||
- AI assistants can load context from local files in a new conversation
|
||||
- Notes are saved locally as Markdown files in real time
|
||||
- No project knowledge or special prompting required
|
||||
|
||||
![[Claude-Obsidian-Demo.mp4]]
|
||||
|
||||
Basic Memory uses:
|
||||
|
||||
- **Files as the source of truth** - Everything is stored in plain Markdown files
|
||||
- **Git-compatible storage** - All knowledge can be versioned, branched, and merged
|
||||
- **Local SQLite database** - For fast indexing and searching only (not primary storage)
|
||||
- **Model Context Protocol (MCP)** - For seamless AI assistant integration
|
||||
|
||||
Basic Memory gives you complete control over your knowledge:
|
||||
|
||||
- **Local-first storage** - All knowledge lives on your computer
|
||||
- **Standard file formats** - Plain Markdown compatible with any editor
|
||||
- **Directory organization** - Knowledge stored in `~/basic-memory` by default
|
||||
- **Version control ready** - Use git for history, branching, and collaboration
|
||||
- **Edit anywhere** - Modify files with any text editor or Obsidian
|
||||
|
||||
Changes to files automatically sync with the knowledge graph, and AI assistants can see your edits in conversations.
|
||||
|
||||
## Documentation Map
|
||||
|
||||
Continue exploring Basic Memory with these guides:
|
||||
|
||||
- Installation and setup [[Getting Started with Basic Memory]]
|
||||
- Comprehensive usage instructions [[User Guide]]
|
||||
- Detailed explanation of knowledge structure [[Knowledge Format]]
|
||||
- Obsidian integration guide [[Obsidian Integration]]
|
||||
- Canvas visualization guide [[Canvas]]
|
||||
- Command line tool reference [[CLI Reference]]
|
||||
- Reference for AI assistants using Basic Memory [[AI Assistant Guide]]
|
||||
- Technical implementation details [[Technical Information]]
|
||||
|
||||
## Next Steps
|
||||
|
||||
Start with the [[Getting Started with Basic Memory]] guide to install Basic Memory and configure it with your AI
|
||||
assistant.
|
||||
|
Before Width: | Height: | Size: 374 KiB |
|
Before Width: | Height: | Size: 908 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 302 KiB |
|
Before Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,241 @@
|
||||
# Character Handling and Conflict Resolution
|
||||
|
||||
Basic Memory handles various character encoding scenarios and file naming conventions to provide consistent permalink generation and conflict resolution. This document explains how the system works and how to resolve common character-related issues.
|
||||
|
||||
## Overview
|
||||
|
||||
Basic Memory uses a sophisticated system to generate permalinks from file paths while maintaining consistency across different operating systems and character encodings. The system normalizes file paths and generates unique permalinks to prevent conflicts.
|
||||
|
||||
## Character Normalization Rules
|
||||
|
||||
### 1. Permalink Generation
|
||||
|
||||
When Basic Memory processes a file path, it applies these normalization rules:
|
||||
|
||||
```
|
||||
Original: "Finance/My Investment Strategy.md"
|
||||
Permalink: "finance/my-investment-strategy"
|
||||
```
|
||||
|
||||
**Transformation process:**
|
||||
1. Remove file extension (`.md`)
|
||||
2. Convert to lowercase (case-insensitive)
|
||||
3. Replace spaces with hyphens
|
||||
4. Replace underscores with hyphens
|
||||
5. Handle international characters (transliteration for Latin, preservation for non-Latin)
|
||||
6. Convert camelCase to kebab-case
|
||||
|
||||
### 2. International Character Support
|
||||
|
||||
**Latin characters with diacritics** are transliterated:
|
||||
- `ø` → `o` (Søren → soren)
|
||||
- `ü` → `u` (Müller → muller)
|
||||
- `é` → `e` (Café → cafe)
|
||||
- `ñ` → `n` (Niño → nino)
|
||||
|
||||
**Non-Latin characters** are preserved:
|
||||
- Chinese: `中文/测试文档.md` → `中文/测试文档`
|
||||
- Japanese: `日本語/文書.md` → `日本語/文書`
|
||||
|
||||
## Common Conflict Scenarios
|
||||
|
||||
### 1. Hyphen vs Space Conflicts
|
||||
|
||||
**Problem:** Files with existing hyphens conflict with generated permalinks from spaces.
|
||||
|
||||
**Example:**
|
||||
```
|
||||
File 1: "basic memory bug.md" → permalink: "basic-memory-bug"
|
||||
File 2: "basic-memory-bug.md" → permalink: "basic-memory-bug" (CONFLICT!)
|
||||
```
|
||||
|
||||
**Resolution:** The system automatically resolves this by adding suffixes:
|
||||
```
|
||||
File 1: "basic memory bug.md" → permalink: "basic-memory-bug"
|
||||
File 2: "basic-memory-bug.md" → permalink: "basic-memory-bug-1"
|
||||
```
|
||||
|
||||
**Best Practice:** Choose consistent naming conventions within your project.
|
||||
|
||||
### 2. Case Sensitivity Conflicts
|
||||
|
||||
**Problem:** Different case variations that normalize to the same permalink.
|
||||
|
||||
**Example on macOS:**
|
||||
```
|
||||
Directory: Finance/investment.md
|
||||
Directory: finance/investment.md (different on filesystem, same permalink)
|
||||
```
|
||||
|
||||
**Resolution:** Basic Memory detects case conflicts and prevents them during sync operations with helpful error messages.
|
||||
|
||||
**Best Practice:** Use consistent casing for directory and file names.
|
||||
|
||||
### 3. Character Encoding Conflicts
|
||||
|
||||
**Problem:** Different Unicode normalizations of the same logical character.
|
||||
|
||||
**Example:**
|
||||
```
|
||||
File 1: "café.md" (é as single character)
|
||||
File 2: "café.md" (e + combining accent)
|
||||
```
|
||||
|
||||
**Resolution:** Basic Memory normalizes Unicode characters using NFD normalization to detect these conflicts.
|
||||
|
||||
### 4. Forward Slash Conflicts
|
||||
|
||||
**Problem:** Forward slashes in frontmatter or file names interpreted as path separators.
|
||||
|
||||
**Example:**
|
||||
```yaml
|
||||
---
|
||||
permalink: finance/investment/strategy
|
||||
---
|
||||
```
|
||||
|
||||
**Resolution:** Basic Memory validates frontmatter permalinks and warns about path separator conflicts.
|
||||
|
||||
## Error Messages and Troubleshooting
|
||||
|
||||
### "UNIQUE constraint failed: entity.file_path, entity.project_id"
|
||||
|
||||
**Cause:** Two entities trying to use the same file path within a project.
|
||||
|
||||
**Common scenarios:**
|
||||
1. File move operation where destination is already occupied
|
||||
2. Case sensitivity differences on macOS
|
||||
3. Character encoding conflicts
|
||||
4. Concurrent file operations
|
||||
|
||||
**Resolution steps:**
|
||||
1. Check for duplicate file names with different cases
|
||||
2. Look for files with similar names but different character encodings
|
||||
3. Rename conflicting files to have unique names
|
||||
4. Run sync again after resolving conflicts
|
||||
|
||||
### "File path conflict detected during move"
|
||||
|
||||
**Cause:** Enhanced conflict detection preventing potential database integrity violations.
|
||||
|
||||
**What this means:** The system detected that moving a file would create a conflict before attempting the database operation.
|
||||
|
||||
**Resolution:** Follow the specific guidance in the error message, which will indicate the type of conflict detected.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. File Naming Conventions
|
||||
|
||||
**Recommended patterns:**
|
||||
- Use consistent casing (prefer lowercase)
|
||||
- Use hyphens instead of spaces for multi-word files
|
||||
- Avoid special characters that could conflict with path separators
|
||||
- Be consistent with directory structure casing
|
||||
|
||||
**Examples:**
|
||||
```
|
||||
✅ Good:
|
||||
- finance/investment-strategy.md
|
||||
- projects/basic-memory-features.md
|
||||
- docs/api-reference.md
|
||||
|
||||
❌ Problematic:
|
||||
- Finance/Investment Strategy.md (mixed case, spaces)
|
||||
- finance/Investment Strategy.md (inconsistent case)
|
||||
- docs/API/Reference.md (mixed case directories)
|
||||
```
|
||||
|
||||
### 2. Permalink Management
|
||||
|
||||
**Custom permalinks in frontmatter:**
|
||||
```yaml
|
||||
---
|
||||
type: knowledge
|
||||
permalink: custom-permalink-name
|
||||
---
|
||||
```
|
||||
|
||||
**Guidelines:**
|
||||
- Use lowercase permalinks
|
||||
- Use hyphens for word separation
|
||||
- Avoid path separators unless creating sub-paths
|
||||
- Ensure uniqueness within your project
|
||||
|
||||
### 3. Directory Structure
|
||||
|
||||
**Consistent casing:**
|
||||
```
|
||||
✅ Good:
|
||||
finance/
|
||||
investment-strategies.md
|
||||
portfolio-management.md
|
||||
|
||||
❌ Problematic:
|
||||
Finance/ (capital F)
|
||||
investment-strategies.md
|
||||
finance/ (lowercase f)
|
||||
portfolio-management.md
|
||||
```
|
||||
|
||||
## Migration and Cleanup
|
||||
|
||||
### Identifying Conflicts
|
||||
|
||||
Use Basic Memory's built-in conflict detection:
|
||||
|
||||
```bash
|
||||
# Sync will report conflicts
|
||||
basic-memory sync
|
||||
|
||||
# Check sync status for warnings
|
||||
basic-memory status
|
||||
```
|
||||
|
||||
### Resolving Existing Conflicts
|
||||
|
||||
1. **Identify conflicting files** from sync error messages
|
||||
2. **Choose consistent naming convention** for your project
|
||||
3. **Rename files** to follow the convention
|
||||
4. **Re-run sync** to verify resolution
|
||||
|
||||
### Bulk Renaming Strategy
|
||||
|
||||
For projects with many conflicts:
|
||||
|
||||
1. **Backup your project** before making changes
|
||||
2. **Standardize on lowercase** file and directory names
|
||||
3. **Replace spaces with hyphens** in file names
|
||||
4. **Use consistent character encoding** (UTF-8)
|
||||
5. **Test sync after each batch** of changes
|
||||
|
||||
## System Enhancements
|
||||
|
||||
### Recent Improvements (v0.13+)
|
||||
|
||||
1. **Enhanced conflict detection** before database operations
|
||||
2. **Improved error messages** with specific resolution guidance
|
||||
3. **Character normalization utilities** for consistent handling
|
||||
4. **File swap detection** for complex move scenarios
|
||||
5. **Proactive conflict warnings** during permalink resolution
|
||||
|
||||
### Monitoring and Logging
|
||||
|
||||
The system now provides detailed logging for conflict resolution:
|
||||
|
||||
```
|
||||
DEBUG: Detected potential file path conflicts for 'Finance/Investment.md': ['finance/investment.md']
|
||||
WARNING: File path conflict detected during move: entity_id=123 trying to move from 'old.md' to 'new.md'
|
||||
```
|
||||
|
||||
These logs help identify and resolve conflicts before they cause sync failures.
|
||||
|
||||
## Support and Resources
|
||||
|
||||
If you encounter character-related conflicts not covered in this guide:
|
||||
|
||||
1. **Check the logs** for specific conflict details
|
||||
2. **Review error messages** for resolution guidance
|
||||
3. **Report issues** with examples of the conflicting files
|
||||
4. **Consider the file naming best practices** outlined above
|
||||
|
||||
The Basic Memory system is designed to handle most character conflicts automatically while providing clear guidance for manual resolution when needed.
|
||||
@@ -0,0 +1,726 @@
|
||||
# Basic Memory Cloud CLI Guide
|
||||
|
||||
The Basic Memory Cloud CLI provides seamless integration between local and cloud knowledge bases using **project-scoped synchronization**. Each project can optionally sync with the cloud, giving you fine-grained control over what syncs and where.
|
||||
|
||||
## Overview
|
||||
|
||||
The cloud CLI enables you to:
|
||||
- **Toggle cloud mode** - All regular `bm` commands work with cloud when enabled
|
||||
- **Project-scoped sync** - Each project independently manages its sync configuration
|
||||
- **Explicit operations** - Sync only what you want, when you want
|
||||
- **Bidirectional sync** - Keep local and cloud in sync with rclone bisync
|
||||
- **Offline access** - Work locally, sync when ready
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before using Basic Memory Cloud, you need:
|
||||
|
||||
- **Active Subscription**: An active Basic Memory Cloud subscription is required to access cloud features
|
||||
- **Subscribe**: Visit [https://basicmemory.com/subscribe](https://basicmemory.com/subscribe) to sign up
|
||||
|
||||
If you attempt to log in without an active subscription, you'll receive a "Subscription Required" error with a link to subscribe.
|
||||
|
||||
## Architecture: Project-Scoped Sync
|
||||
|
||||
### The Problem
|
||||
|
||||
**Old approach (SPEC-8):** All projects lived in a single `~/basic-memory-cloud-sync/` directory. This caused:
|
||||
- ❌ Directory conflicts between mount and bisync
|
||||
- ❌ Auto-discovery creating phantom projects
|
||||
- ❌ Confusion about what syncs and when
|
||||
- ❌ All-or-nothing sync (couldn't sync just one project)
|
||||
|
||||
**New approach (SPEC-20):** Each project independently configures sync.
|
||||
|
||||
### How It Works
|
||||
|
||||
**Projects can exist in three states:**
|
||||
|
||||
1. **Cloud-only** - Project exists on cloud, no local copy
|
||||
2. **Cloud + Local (synced)** - Project has a local working directory that syncs
|
||||
3. **Local-only** - Project exists locally (when cloud mode is disabled)
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# You have 3 projects on cloud:
|
||||
# - research: wants local sync at ~/Documents/research
|
||||
# - work: wants local sync at ~/work-notes
|
||||
# - temp: cloud-only, no local sync needed
|
||||
|
||||
bm project add research --local-path ~/Documents/research
|
||||
bm project add work --local-path ~/work-notes
|
||||
bm project add temp # No local sync
|
||||
|
||||
# Now you can sync individually (after initial --resync):
|
||||
bm project bisync --name research
|
||||
bm project bisync --name work
|
||||
# temp stays cloud-only
|
||||
```
|
||||
|
||||
**What happens under the covers:**
|
||||
- Config stores `cloud_projects` dict mapping project names to local paths
|
||||
- Each project gets its own bisync state in `~/.basic-memory/bisync-state/{project}/`
|
||||
- Rclone syncs using single remote: `basic-memory-cloud`
|
||||
- Projects can live anywhere on your filesystem, not forced into sync directory
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Enable Cloud Mode
|
||||
|
||||
Authenticate and enable cloud mode:
|
||||
|
||||
```bash
|
||||
bm cloud login
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
1. Opens browser to Basic Memory Cloud authentication page
|
||||
2. Stores authentication token in `~/.basic-memory/auth/token`
|
||||
3. **Enables cloud mode** - all CLI commands now work against cloud
|
||||
4. Validates your subscription status
|
||||
|
||||
**Result:** All `bm project`, `bm tools` commands now work with cloud.
|
||||
|
||||
### 2. Set Up Sync
|
||||
|
||||
Install rclone and configure credentials:
|
||||
|
||||
```bash
|
||||
bm cloud setup
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
1. Installs rclone automatically (if needed)
|
||||
2. Fetches your tenant information from cloud
|
||||
3. Generates scoped S3 credentials for sync
|
||||
4. Configures single rclone remote: `basic-memory-cloud`
|
||||
|
||||
**Result:** You're ready to sync projects. No sync directories created yet - those come with project setup.
|
||||
|
||||
### 3. Add Projects with Sync
|
||||
|
||||
Create projects with optional local sync paths:
|
||||
|
||||
```bash
|
||||
# Create cloud project without local sync
|
||||
bm project add research
|
||||
|
||||
# Create cloud project WITH local sync
|
||||
bm project add research --local-path ~/Documents/research
|
||||
|
||||
# Or configure sync for existing project
|
||||
bm project sync-setup research ~/Documents/research
|
||||
```
|
||||
|
||||
**What happens under the covers:**
|
||||
|
||||
When you add a project with `--local-path`:
|
||||
1. Project created on cloud at `/app/data/research`
|
||||
2. Local path stored in config: `cloud_projects.research.local_path = "~/Documents/research"`
|
||||
3. Local directory created if it doesn't exist
|
||||
4. Bisync state directory created at `~/.basic-memory/bisync-state/research/`
|
||||
|
||||
**Result:** Project is ready to sync, but no files synced yet.
|
||||
|
||||
### 4. Sync Your Project
|
||||
|
||||
Establish the initial sync baseline. **Best practice:** Always preview with `--dry-run` first:
|
||||
|
||||
```bash
|
||||
# Step 1: Preview the initial sync (recommended)
|
||||
bm project bisync --name research --resync --dry-run
|
||||
|
||||
# Step 2: If all looks good, run the actual sync
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**What happens under the covers:**
|
||||
1. Rclone reads from `~/Documents/research` (local)
|
||||
2. Connects to `basic-memory-cloud:bucket-name/app/data/research` (remote)
|
||||
3. Creates bisync state files in `~/.basic-memory/bisync-state/research/`
|
||||
4. Syncs files bidirectionally with settings:
|
||||
- `conflict_resolve=newer` (most recent wins)
|
||||
- `max_delete=25` (safety limit)
|
||||
- Respects `.bmignore` patterns
|
||||
|
||||
**Result:** Local and cloud are in sync. Baseline established.
|
||||
|
||||
**Why `--resync`?** This is an rclone requirement for the first bisync run. It establishes the initial state that future syncs will compare against. After the first sync, never use `--resync` unless you need to force a new baseline.
|
||||
|
||||
See: https://rclone.org/bisync/#resync
|
||||
```
|
||||
--resync
|
||||
This will effectively make both Path1 and Path2 filesystems contain a matching superset of all files. By default, Path2 files that do not exist in Path1 will be copied to Path1, and the process will then copy the Path1 tree to Path2.
|
||||
```
|
||||
|
||||
### 5. Subsequent Syncs
|
||||
|
||||
After the first sync, just run bisync without `--resync`:
|
||||
|
||||
```bash
|
||||
bm project bisync --name research
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Rclone compares local and cloud states
|
||||
2. Syncs changes in both directions
|
||||
3. Auto-resolves conflicts (newer file wins)
|
||||
4. Updates `last_sync` timestamp in config
|
||||
|
||||
**Result:** Changes flow both ways - edit locally or in cloud, both stay in sync.
|
||||
|
||||
### 6. Verify Setup
|
||||
|
||||
Check status:
|
||||
|
||||
```bash
|
||||
bm cloud status
|
||||
```
|
||||
|
||||
You should see:
|
||||
- `Mode: Cloud (enabled)`
|
||||
- `Cloud instance is healthy`
|
||||
- Instructions for project sync commands
|
||||
|
||||
## Working with Projects
|
||||
|
||||
### Understanding Project Commands
|
||||
|
||||
**Key concept:** When cloud mode is enabled, use regular `bm project` commands (not `bm cloud project`).
|
||||
|
||||
```bash
|
||||
# In cloud mode:
|
||||
bm project list # Lists cloud projects
|
||||
bm project add research # Creates cloud project
|
||||
|
||||
# In local mode:
|
||||
bm project list # Lists local projects
|
||||
bm project add research ~/Documents/research # Creates local project
|
||||
```
|
||||
|
||||
### Creating Projects
|
||||
|
||||
**Use case 1: Cloud-only project (no local sync)**
|
||||
|
||||
```bash
|
||||
bm project add temp-notes
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Creates project on cloud at `/app/data/temp-notes`
|
||||
- No local directory created
|
||||
- No sync configuration
|
||||
|
||||
**Result:** Project exists on cloud, accessible via MCP tools, but no local copy.
|
||||
|
||||
**Use case 2: Cloud project with local sync**
|
||||
|
||||
```bash
|
||||
bm project add research --local-path ~/Documents/research
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Creates project on cloud at `/app/data/research`
|
||||
- Creates local directory `~/Documents/research`
|
||||
- Stores sync config in `~/.basic-memory/config.json`
|
||||
- Prepares for bisync (but doesn't sync yet)
|
||||
|
||||
**Result:** Project ready to sync. Run `bm project bisync --name research --resync` to establish baseline.
|
||||
|
||||
**Use case 3: Add sync to existing cloud project**
|
||||
|
||||
```bash
|
||||
# Project already exists on cloud
|
||||
bm project sync-setup research ~/Documents/research
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Updates existing project's sync configuration
|
||||
- Creates local directory
|
||||
- Prepares for bisync
|
||||
|
||||
**Result:** Existing cloud project now has local sync path. Run bisync to pull files down.
|
||||
|
||||
### Listing Projects
|
||||
|
||||
View all projects:
|
||||
|
||||
```bash
|
||||
bm project list
|
||||
```
|
||||
|
||||
**What you see:**
|
||||
- All projects in cloud (when cloud mode enabled)
|
||||
- Default project marked
|
||||
- Project paths shown
|
||||
|
||||
**Future:** Will show sync status (synced/not synced, last sync time).
|
||||
|
||||
## File Synchronization
|
||||
|
||||
### Understanding the Sync Commands
|
||||
|
||||
**There are three sync-related commands:**
|
||||
|
||||
1. `bm project sync` - One-way: local → cloud (make cloud match local)
|
||||
2. `bm project bisync` - Two-way: local ↔ cloud (recommended)
|
||||
3. `bm project check` - Verify files match (no changes)
|
||||
|
||||
### One-Way Sync: Local → Cloud
|
||||
|
||||
**Use case:** You made changes locally and want to push to cloud (overwrite cloud).
|
||||
|
||||
```bash
|
||||
bm project sync --name research
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Reads files from `~/Documents/research` (local)
|
||||
2. Uses rclone sync to make cloud identical to local
|
||||
3. Respects `.bmignore` patterns
|
||||
4. Shows progress bar
|
||||
|
||||
**Result:** Cloud now matches local exactly. Any cloud-only changes are overwritten.
|
||||
|
||||
**When to use:**
|
||||
- You know local is the source of truth
|
||||
- You want to force cloud to match local
|
||||
- You don't care about cloud changes
|
||||
|
||||
### Two-Way Sync: Local ↔ Cloud (Recommended)
|
||||
|
||||
**Use case:** You edit files both locally and in cloud UI, want both to stay in sync.
|
||||
|
||||
```bash
|
||||
# First time - establish baseline
|
||||
bm project bisync --name research --resync
|
||||
|
||||
# Subsequent syncs
|
||||
bm project bisync --name research
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Compares local and cloud states using bisync metadata
|
||||
2. Syncs changes in both directions
|
||||
3. Auto-resolves conflicts (newer file wins)
|
||||
4. Detects excessive deletes and fails safely (max 25 files)
|
||||
|
||||
**Conflict resolution example:**
|
||||
|
||||
```bash
|
||||
# Edit locally
|
||||
echo "Local change" > ~/Documents/research/notes.md
|
||||
|
||||
# Edit same file in cloud UI
|
||||
# Cloud now has: "Cloud change"
|
||||
|
||||
# Run bisync
|
||||
bm project bisync --name research
|
||||
|
||||
# Result: Newer file wins (based on modification time)
|
||||
# If cloud was more recent, cloud version kept
|
||||
# If local was more recent, local version kept
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
- Default workflow for most users
|
||||
- You edit in multiple places
|
||||
- You want automatic conflict resolution
|
||||
|
||||
### Verify Sync Integrity
|
||||
|
||||
**Use case:** Check if local and cloud match without making changes.
|
||||
|
||||
```bash
|
||||
bm project check --name research
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Compares file checksums between local and cloud
|
||||
2. Reports differences
|
||||
3. No files transferred
|
||||
|
||||
**Result:** Shows which files differ. Run bisync to sync them.
|
||||
|
||||
```bash
|
||||
# One-way check (faster)
|
||||
bm project check --name research --one-way
|
||||
```
|
||||
|
||||
### Preview Changes (Dry Run)
|
||||
|
||||
**Use case:** See what would change without actually syncing.
|
||||
|
||||
```bash
|
||||
bm project bisync --name research --dry-run
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Runs bisync logic
|
||||
2. Shows what would be transferred/deleted
|
||||
3. No actual changes made
|
||||
|
||||
**Result:** Safe preview of sync operations.
|
||||
|
||||
### Advanced: List Remote Files
|
||||
|
||||
**Use case:** See what files exist on cloud without syncing.
|
||||
|
||||
```bash
|
||||
# List all files in project
|
||||
bm project ls --name research
|
||||
|
||||
# List files in subdirectory
|
||||
bm project ls --name research --path subfolder
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
1. Connects to cloud via rclone
|
||||
2. Lists files in remote project path
|
||||
3. No files transferred
|
||||
|
||||
**Result:** See cloud file listing.
|
||||
|
||||
## Multiple Projects
|
||||
|
||||
### Syncing Multiple Projects
|
||||
|
||||
**Use case:** You have several projects with local sync, want to sync all at once.
|
||||
|
||||
```bash
|
||||
# Setup multiple projects
|
||||
bm project add research --local-path ~/Documents/research
|
||||
bm project add work --local-path ~/work-notes
|
||||
bm project add personal --local-path ~/personal
|
||||
|
||||
# Establish baselines
|
||||
bm project bisync --name research --resync
|
||||
bm project bisync --name work --resync
|
||||
bm project bisync --name personal --resync
|
||||
|
||||
# Daily workflow: sync everything
|
||||
bm project bisync --name research
|
||||
bm project bisync --name work
|
||||
bm project bisync --name personal
|
||||
```
|
||||
|
||||
**Future:** `--all` flag will sync all configured projects:
|
||||
|
||||
```bash
|
||||
bm project bisync --all # Coming soon
|
||||
```
|
||||
|
||||
### Mixed Usage
|
||||
|
||||
**Use case:** Some projects sync, some stay cloud-only.
|
||||
|
||||
```bash
|
||||
# Projects with sync
|
||||
bm project add research --local-path ~/Documents/research
|
||||
bm project add work --local-path ~/work
|
||||
|
||||
# Cloud-only projects
|
||||
bm project add archive
|
||||
bm project add temp-notes
|
||||
|
||||
# Sync only the configured ones
|
||||
bm project bisync --name research
|
||||
bm project bisync --name work
|
||||
|
||||
# Archive and temp-notes stay cloud-only
|
||||
```
|
||||
|
||||
**Result:** Fine-grained control over what syncs.
|
||||
|
||||
## Disable Cloud Mode
|
||||
|
||||
Return to local mode:
|
||||
|
||||
```bash
|
||||
bm cloud logout
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
1. Disables cloud mode in config
|
||||
2. All commands now work locally
|
||||
3. Auth token remains (can re-enable with login)
|
||||
|
||||
**Result:** All `bm` commands work with local projects again.
|
||||
|
||||
## Filter Configuration
|
||||
|
||||
### Understanding .bmignore
|
||||
|
||||
**The problem:** You don't want to sync everything (e.g., `.git`, `node_modules`, database files).
|
||||
|
||||
**The solution:** `.bmignore` file with gitignore-style patterns.
|
||||
|
||||
**Location:** `~/.basic-memory/.bmignore`
|
||||
|
||||
**Default patterns:**
|
||||
|
||||
```gitignore
|
||||
# Version control
|
||||
.git/**
|
||||
|
||||
# Python
|
||||
__pycache__/**
|
||||
*.pyc
|
||||
.venv/**
|
||||
venv/**
|
||||
|
||||
# Node.js
|
||||
node_modules/**
|
||||
|
||||
# Basic Memory internals
|
||||
memory.db/**
|
||||
memory.db-shm/**
|
||||
memory.db-wal/**
|
||||
config.json/**
|
||||
watch-status.json/**
|
||||
.bmignore.rclone/**
|
||||
|
||||
# OS files
|
||||
.DS_Store/**
|
||||
Thumbs.db/**
|
||||
|
||||
# Environment files
|
||||
.env/**
|
||||
.env.local/**
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
1. On first sync, `.bmignore` created with defaults
|
||||
2. Patterns converted to rclone filter format (`.bmignore.rclone`)
|
||||
3. Rclone uses filters during sync
|
||||
4. Same patterns used by all projects
|
||||
|
||||
**Customizing:**
|
||||
|
||||
```bash
|
||||
# Edit patterns
|
||||
code ~/.basic-memory/.bmignore
|
||||
|
||||
# Add custom patterns
|
||||
echo "*.tmp/**" >> ~/.basic-memory/.bmignore
|
||||
|
||||
# Next sync uses updated patterns
|
||||
bm project bisync --name research
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Issues
|
||||
|
||||
**Problem:** "Authentication failed" or "Invalid token"
|
||||
|
||||
**Solution:** Re-authenticate:
|
||||
|
||||
```bash
|
||||
bm cloud logout
|
||||
bm cloud login
|
||||
```
|
||||
|
||||
### Subscription Issues
|
||||
|
||||
**Problem:** "Subscription Required" error
|
||||
|
||||
**Solution:**
|
||||
1. Visit subscribe URL shown in error
|
||||
2. Sign up for subscription
|
||||
3. Run `bm cloud login` again
|
||||
|
||||
**Note:** Access is immediate when subscription becomes active.
|
||||
|
||||
### Bisync Initialization
|
||||
|
||||
**Problem:** "First bisync requires --resync"
|
||||
|
||||
**Explanation:** Bisync needs a baseline state before it can sync changes.
|
||||
|
||||
**Solution:**
|
||||
|
||||
```bash
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Establishes initial sync state
|
||||
- Creates baseline in `~/.basic-memory/bisync-state/research/`
|
||||
- Syncs all files bidirectionally
|
||||
|
||||
**Result:** Future syncs work without `--resync`.
|
||||
|
||||
### Empty Directory Issues
|
||||
|
||||
**Problem:** "Empty prior Path1 listing. Cannot sync to an empty directory"
|
||||
|
||||
**Explanation:** Rclone bisync doesn't work well with completely empty directories. It needs at least one file to establish a baseline.
|
||||
|
||||
**Solution:** Add at least one file before running `--resync`:
|
||||
|
||||
```bash
|
||||
# Create a placeholder file
|
||||
echo "# Research Notes" > ~/Documents/research/README.md
|
||||
|
||||
# Now run bisync
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**Why this happens:** Bisync creates listing files that track the state of each side. When both directories are completely empty, these listing files are considered invalid by rclone.
|
||||
|
||||
**Best practice:** Always have at least one file (like a README.md) in your project directory before setting up sync.
|
||||
|
||||
### Bisync State Corruption
|
||||
|
||||
**Problem:** Bisync fails with errors about corrupted state or listing files
|
||||
|
||||
**Explanation:** Sometimes bisync state can become inconsistent (e.g., after mixing dry-run and actual runs, or after manual file operations).
|
||||
|
||||
**Solution:** Clear bisync state and re-establish baseline:
|
||||
|
||||
```bash
|
||||
# Clear bisync state
|
||||
bm project bisync-reset research
|
||||
|
||||
# Re-establish baseline
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
- Removes all bisync metadata from `~/.basic-memory/bisync-state/research/`
|
||||
- Forces fresh baseline on next `--resync`
|
||||
- Safe operation (doesn't touch your files)
|
||||
|
||||
**Note:** This command also runs automatically when you remove a project to clean up state directories.
|
||||
|
||||
### Too Many Deletes
|
||||
|
||||
**Problem:** "Error: max delete limit (25) exceeded"
|
||||
|
||||
**Explanation:** Bisync detected you're about to delete more than 25 files. This is a safety check to prevent accidents.
|
||||
|
||||
**Solution 1:** Review what you're deleting, then force resync:
|
||||
|
||||
```bash
|
||||
# Check what would be deleted
|
||||
bm project bisync --name research --dry-run
|
||||
|
||||
# If correct, establish new baseline
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
**Solution 2:** Use one-way sync if you know local is correct:
|
||||
|
||||
```bash
|
||||
bm project sync --name research
|
||||
```
|
||||
|
||||
### Project Not Configured for Sync
|
||||
|
||||
**Problem:** "Project research has no local_sync_path configured"
|
||||
|
||||
**Explanation:** Project exists on cloud but has no local sync path.
|
||||
|
||||
**Solution:**
|
||||
|
||||
```bash
|
||||
bm project sync-setup research ~/Documents/research
|
||||
bm project bisync --name research --resync
|
||||
```
|
||||
|
||||
### Connection Issues
|
||||
|
||||
**Problem:** "Cannot connect to cloud instance"
|
||||
|
||||
**Solution:** Check status:
|
||||
|
||||
```bash
|
||||
bm cloud status
|
||||
```
|
||||
|
||||
If instance is down, wait a few minutes and retry.
|
||||
|
||||
## Security
|
||||
|
||||
- **Authentication**: OAuth 2.1 with PKCE flow
|
||||
- **Tokens**: Stored securely in `~/.basic-memory/basic-memory-cloud.json`
|
||||
- **Transport**: All data encrypted in transit (HTTPS)
|
||||
- **Credentials**: Scoped S3 credentials (read-write to your tenant only)
|
||||
- **Isolation**: Your data isolated from other tenants
|
||||
- **Ignore patterns**: Sensitive files automatically excluded via `.bmignore`
|
||||
|
||||
## Command Reference
|
||||
|
||||
### Cloud Mode Management
|
||||
|
||||
```bash
|
||||
bm cloud login # Authenticate and enable cloud mode
|
||||
bm cloud logout # Disable cloud mode
|
||||
bm cloud status # Check cloud mode and instance health
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
bm cloud setup # Install rclone and configure credentials
|
||||
```
|
||||
|
||||
### Project Management
|
||||
|
||||
When cloud mode is enabled:
|
||||
|
||||
```bash
|
||||
bm project list # List cloud projects
|
||||
bm project add <name> # Create cloud project (no sync)
|
||||
bm project add <name> --local-path <path> # Create with local sync
|
||||
bm project sync-setup <name> <path> # Add sync to existing project
|
||||
bm project rm <name> # Delete project
|
||||
```
|
||||
|
||||
### File Synchronization
|
||||
|
||||
```bash
|
||||
# One-way sync (local → cloud)
|
||||
bm project sync --name <project>
|
||||
bm project sync --name <project> --dry-run
|
||||
bm project sync --name <project> --verbose
|
||||
|
||||
# Two-way sync (local ↔ cloud) - Recommended
|
||||
bm project bisync --name <project> # After first --resync
|
||||
bm project bisync --name <project> --resync # First time / force baseline
|
||||
bm project bisync --name <project> --dry-run
|
||||
bm project bisync --name <project> --verbose
|
||||
|
||||
# Integrity check
|
||||
bm project check --name <project>
|
||||
bm project check --name <project> --one-way
|
||||
|
||||
# List remote files
|
||||
bm project ls --name <project>
|
||||
bm project ls --name <project> --path <subpath>
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Basic Memory Cloud uses project-scoped sync:**
|
||||
|
||||
1. **Enable cloud mode** - `bm cloud login`
|
||||
2. **Install rclone** - `bm cloud setup`
|
||||
3. **Add projects with sync** - `bm project add research --local-path ~/Documents/research`
|
||||
4. **Preview first sync** - `bm project bisync --name research --resync --dry-run`
|
||||
5. **Establish baseline** - `bm project bisync --name research --resync`
|
||||
6. **Daily workflow** - `bm project bisync --name research`
|
||||
|
||||
**Key benefits:**
|
||||
- ✅ Each project independently syncs (or doesn't)
|
||||
- ✅ Projects can live anywhere on disk
|
||||
- ✅ Explicit sync operations (no magic)
|
||||
- ✅ Safe by design (max delete limits, conflict resolution)
|
||||
- ✅ Full offline access (work locally, sync when ready)
|
||||
|
||||
**Future enhancements:**
|
||||
- `--all` flag to sync all configured projects
|
||||
- Project list showing sync status
|
||||
- Watch mode for automatic sync
|
||||
@@ -1,5 +0,0 @@
|
||||
var analyticsScript = document.createElement('script');
|
||||
analyticsScript.defer = true;
|
||||
analyticsScript.setAttribute('data-website-id', '8d51086e-5c67-401e-97b0-b24706a6d4f3');
|
||||
analyticsScript.src = 'https://cloud.umami.is/script.js';
|
||||
document.head.appendChild(analyticsScript);
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"promptDelete": false
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,3 +0,0 @@
|
||||
[
|
||||
"optimize-canvas-connections"
|
||||
]
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"properties": false,
|
||||
"page-preview": true,
|
||||
"daily-notes": true,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": true,
|
||||
"sync": true,
|
||||
"webviewer": false
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||
if you want to view the source, please visit the github repository of this plugin
|
||||
*/
|
||||
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// main.ts
|
||||
var main_exports = {};
|
||||
__export(main_exports, {
|
||||
default: () => OptimizeCanvasConnectionsPlugin
|
||||
});
|
||||
module.exports = __toCommonJS(main_exports);
|
||||
var import_obsidian = require("obsidian");
|
||||
var OptimizeCanvasConnectionsPlugin = class extends import_obsidian.Plugin {
|
||||
async onload() {
|
||||
this.addCommand({
|
||||
id: "optimize-preserve-axes-selection",
|
||||
name: "Optimize selection (preserve axes)",
|
||||
checkCallback: (checking) => {
|
||||
const canvasView = app.workspace.getActiveViewOfType(import_obsidian.ItemView);
|
||||
if ((canvasView == null ? void 0 : canvasView.getViewType()) == "canvas") {
|
||||
if (!checking) {
|
||||
this.optimize("preserve-axes");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
this.addCommand({
|
||||
id: "optimize-shortest-path-selection",
|
||||
name: "Optimize selection (shortest path)",
|
||||
checkCallback: (checking) => {
|
||||
const canvasView = app.workspace.getActiveViewOfType(import_obsidian.ItemView);
|
||||
if ((canvasView == null ? void 0 : canvasView.getViewType()) == "canvas") {
|
||||
if (!checking) {
|
||||
this.optimize("shortest-path");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
onunload() {
|
||||
}
|
||||
async optimize(option) {
|
||||
const canvasView = app.workspace.getActiveViewOfType(import_obsidian.ItemView);
|
||||
const canvas = canvasView == null ? void 0 : canvasView.canvas;
|
||||
const currentSelection = canvas == null ? void 0 : canvas.selection;
|
||||
let selectedIDs = new Array();
|
||||
currentSelection.forEach(function(selection) {
|
||||
selectedIDs.push(selection.id);
|
||||
});
|
||||
let applyToAll = false;
|
||||
if (selectedIDs.length == 0) {
|
||||
applyToAll = true;
|
||||
}
|
||||
for (let [edgeKey, edge] of canvas["edges"]) {
|
||||
let fromNode = edge["from"]["node"];
|
||||
let toNode = edge["to"]["node"];
|
||||
let fromPossibilities = [edge["from"]["side"]];
|
||||
if (applyToAll || selectedIDs.includes(fromNode["id"])) {
|
||||
switch (option) {
|
||||
case "shortest-path":
|
||||
fromPossibilities = ["top", "bottom", "left", "right"];
|
||||
break;
|
||||
case "preserve-axes":
|
||||
switch (edge["from"]["side"]) {
|
||||
case "top":
|
||||
case "bottom":
|
||||
fromPossibilities = ["top", "bottom"];
|
||||
break;
|
||||
case "left":
|
||||
case "right":
|
||||
fromPossibilities = ["left", "right"];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let toPossibilities = [edge["to"]["side"]];
|
||||
if (applyToAll || selectedIDs.includes(toNode["id"])) {
|
||||
switch (option) {
|
||||
case "shortest-path":
|
||||
toPossibilities = ["top", "bottom", "left", "right"];
|
||||
break;
|
||||
case "preserve-axes":
|
||||
switch (edge["to"]["side"]) {
|
||||
case "top":
|
||||
case "bottom":
|
||||
toPossibilities = ["top", "bottom"];
|
||||
break;
|
||||
case "left":
|
||||
case "right":
|
||||
toPossibilities = ["left", "right"];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let distances = [];
|
||||
for (const fromSide of fromPossibilities) {
|
||||
let fromPoint = { "x": 0, "y": 0 };
|
||||
if (fromSide == "top") {
|
||||
fromPoint = { "x": fromNode["x"] + fromNode["width"] / 2, "y": fromNode["y"] };
|
||||
} else if (fromSide == "bottom") {
|
||||
fromPoint = { "x": fromNode["x"] + fromNode["width"] / 2, "y": fromNode["y"] + fromNode["height"] };
|
||||
} else if (fromSide == "left") {
|
||||
fromPoint = { "x": fromNode["x"], "y": fromNode["y"] + fromNode["height"] / 2 };
|
||||
} else if (fromSide == "right") {
|
||||
fromPoint = { "x": fromNode["x"] + fromNode["width"], "y": fromNode["y"] + fromNode["height"] / 2 };
|
||||
}
|
||||
for (const toSide of toPossibilities) {
|
||||
let toPoint = { "x": 0, "y": 0 };
|
||||
if (toSide == "top") {
|
||||
toPoint = { "x": toNode["x"] + toNode["width"] / 2, "y": toNode["y"] };
|
||||
} else if (toSide == "bottom") {
|
||||
toPoint = { "x": toNode["x"] + toNode["width"] / 2, "y": toNode["y"] + toNode["height"] };
|
||||
} else if (toSide == "left") {
|
||||
toPoint = { "x": toNode["x"], "y": toNode["y"] + toNode["height"] / 2 };
|
||||
} else if (toSide == "right") {
|
||||
toPoint = { "x": toNode["x"] + toNode["width"], "y": toNode["y"] + toNode["height"] / 2 };
|
||||
}
|
||||
distances.push({
|
||||
"fromSide": fromSide,
|
||||
"toSide": toSide,
|
||||
"distance": (toPoint.x - fromPoint.x) ** 2 + (toPoint.y - fromPoint.y) ** 2
|
||||
});
|
||||
}
|
||||
}
|
||||
distances = distances.sort(function(a, b) {
|
||||
return a.distance - b.distance;
|
||||
});
|
||||
edge["from"]["side"] = distances[0]["fromSide"];
|
||||
edge["to"]["side"] = distances[0]["toSide"];
|
||||
edge.render();
|
||||
}
|
||||
canvas.requestSave();
|
||||
}
|
||||
};
|
||||
|
||||
/* nosourcemap */
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"id": "optimize-canvas-connections",
|
||||
"name": "Optimize Canvas Connections",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "1.1.9",
|
||||
"description": "An Obsidian plugin that declutters a canvas by reconnecting notes using their nearest edges.",
|
||||
"author": "Félix Chénier",
|
||||
"authorUrl": "https://felixchenier.uqam.ca",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"siteId": null,
|
||||
"host": null,
|
||||
"included": [],
|
||||
"excluded": []
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
---
|
||||
title: Brewing Equipment
|
||||
type: note
|
||||
permalink: coffee/brewing-equipment
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#equipment'
|
||||
- '#gear'
|
||||
- '#brewing'
|
||||
- '#demo'
|
||||
---
|
||||
|
||||
# Brewing Equipment
|
||||
|
||||
Essential tools and equipment for brewing coffee, their characteristics, and how they affect the brewing process.
|
||||
|
||||
## Overview
|
||||
|
||||
The equipment used to brew coffee plays a crucial role in determining the final cup quality. From grinders to brewers to kettles, each piece of equipment contributes to different aspects of the brewing process.
|
||||
|
||||
## Observations
|
||||
|
||||
- [principle] Equipment quality often has a bigger impact on consistency than on absolute quality potential #quality
|
||||
- [principle] Good grind consistency is the most important technical factor in extraction quality #grind
|
||||
- [investment] A good burr grinder is often the most important investment for improving home coffee #gear
|
||||
- [technique] Equipment maintenance and cleaning significantly impact flavor consistency over time #maintenance
|
||||
|
||||
## Grinders
|
||||
|
||||
- [equipment] Burr grinders crush beans between two abrasive surfaces for more consistent particle size #grinders
|
||||
- [equipment] Blade grinders chop beans unevenly, leading to inconsistent extraction #grinders
|
||||
- [equipment] Flat burr grinders produce very consistent particle size but generate more heat #burrs
|
||||
- [equipment] Conical burr grinders create slightly less uniform grounds but with less heat and noise #burrs
|
||||
- [feature] Grind adjustment mechanisms range from stepped to stepless for different precision levels #adjustment
|
||||
- [feature] Retention (grounds trapped in grinder) affects dose consistency and freshness #retention
|
||||
- [price] Hand grinders offer excellent value, with models like Timemore C2 and 1Zpresso JX providing excellent results around $100-150 #budget
|
||||
- [price] Entry-level electric burr grinders like Baratza Encore start around $170 but provide significant improvement over blade grinders #value
|
||||
|
||||
## Brewers
|
||||
|
||||
### Pour Over Brewers
|
||||
- [equipment] Hario V60 uses a conical design with spiral ridges to control flow rate #pourover
|
||||
- [equipment] Kalita Wave has a flat bottom with three small holes for more consistent extraction #pourover
|
||||
- [equipment] Chemex combines brewer and server with thick proprietary filters for ultra-clean cup #pourover
|
||||
- [material] Ceramic brewers retain heat better than plastic but are more fragile #materials
|
||||
- [material] Glass brewers provide neutral flavor but less heat retention #materials
|
||||
- [material] Plastic brewers are inexpensive, durable, and surprisingly good for heat retention #materials
|
||||
|
||||
### Immersion Brewers
|
||||
- [equipment] French Press uses a metal mesh to separate grounds, allowing oils and fine particles to pass #immersion
|
||||
- [equipment] AeroPress uses pressure and paper filter for clean, versatile brewing #immersion
|
||||
- [equipment] Clever Dripper combines immersion and drip methods with a valve mechanism #hybrid
|
||||
- [material] Glass French presses look elegant but break easily and have poor heat retention #materials
|
||||
- [material] Stainless steel or ceramic French presses offer better durability and heat retention #materials
|
||||
|
||||
### Pressure Brewers
|
||||
- [equipment] Espresso machines use 9 bars of pressure, requiring significant investment for good results #espresso
|
||||
- [equipment] Moka pot uses steam pressure for strong, concentrated coffee at affordable price #moka
|
||||
- [equipment] Manual lever machines like Flair or Robot provide espresso-style coffee with manual control #manual_espresso
|
||||
|
||||
## Kettles
|
||||
|
||||
- [equipment] Gooseneck kettles provide precision pouring control essential for pour over methods #kettles
|
||||
- [feature] Variable temperature kettles allow precise temperature control for different roast levels #temp_control
|
||||
- [feature] Flow restrictors can help beginners maintain consistent pour rates #pour_control
|
||||
- [material] Electric kettles offer convenience and temperature stability #convenience
|
||||
- [material] Stovetop kettles may be more durable but offer less temperature control #durability
|
||||
|
||||
## Accessories
|
||||
|
||||
- [equipment] Coffee scale with 0.1g precision helps maintain consistent ratios #measurement
|
||||
- [equipment] Timer ensures consistent extraction times #consistency
|
||||
- [equipment] Quality filters significantly impact flavor clarity and body #filters
|
||||
- [equipment] Storage containers with one-way valves help preserve bean freshness #storage
|
||||
- [equipment] Blind shaker or dosing cup reduces grinder mess and improves workflow #workflow
|
||||
|
||||
## Relations
|
||||
|
||||
- improves [[Coffee Brewing Methods]]
|
||||
- affects [[Flavor Extraction]]
|
||||
- requires [[Proper Maintenance]]
|
||||
- enhances [[Home Coffee Setup]]
|
||||
- part_of [[Coffee Knowledge Base]]
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
title: Coffee Bean Origins
|
||||
type: note
|
||||
permalink: coffee/coffee-bean-origins
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#origins'
|
||||
- '#beans'
|
||||
- '#regions'
|
||||
- '#demo'
|
||||
---
|
||||
|
||||
# Coffee Bean Origins
|
||||
|
||||
An exploration of coffee-growing regions around the world and how geography, climate, and processing methods affect flavor profiles.
|
||||
|
||||
## Overview
|
||||
|
||||
Coffee beans are grown in various regions around the world, primarily in what's known as the "Coffee Belt" - the area between the Tropics of Cancer and Capricorn. The flavor characteristics of coffee beans are influenced by:
|
||||
|
||||
- Geographic region and climate
|
||||
- Altitude
|
||||
- Soil composition
|
||||
- Variety of coffee plant
|
||||
- Processing method
|
||||
- Harvest and sorting practices
|
||||
|
||||
## Observations
|
||||
|
||||
- [principle] Higher altitude generally produces harder, denser beans with more complex acidity #altitude
|
||||
- [region] Ethiopian beans often feature bright, fruity notes with floral aromatics #ethiopia
|
||||
- [region] Colombian coffee typically offers balanced acidity with caramel sweetness and nutty undertones #colombia
|
||||
- [region] Guatemalan coffee presents complex acidity with chocolate notes and sometimes spice characteristics #guatemala
|
||||
- [region] Brazilian coffee tends toward nutty, chocolate notes with lower acidity and fuller body #brazil
|
||||
- [region] Kenyan coffee is known for bright, wine-like acidity and berry or citrus notes #kenya
|
||||
- [processing] Natural (dry) processing tends to create fruitier, more fermented flavors #processing
|
||||
- [processing] Washed (wet) processing generally results in cleaner, brighter cups with more clarity #processing
|
||||
- [processing] Honey processing creates a middle ground with some fruity notes while maintaining clarity #processing
|
||||
- [factor] Shade-grown coffee typically develops more slowly, resulting in more complex flavors #cultivation
|
||||
- [factor] Soil volcanic soil often imparts distinctive mineral characteristics to coffee #terroir
|
||||
- [variety] Gesha/Geisha variety is known for exceptional floral and tea-like qualities #varieties
|
||||
- [variety] Bourbon varieties often feature sweet, complex cup profiles #varieties
|
||||
- [variety] Robusta beans have higher caffeine content but generally less complex flavor than Arabica #varieties
|
||||
|
||||
## Major Growing Regions
|
||||
|
||||
- [africa] Ethiopian coffees: Yirgacheffe, Sidamo, Harrar regions each with distinctive profiles #ethiopia
|
||||
- [africa] Kenyan coffees: Often categorized by grade (AA, AB, etc.) based on bean size #kenya
|
||||
- [americas] Colombian regions: Huila, Nariño, Antioquia each with unique characteristics #colombia
|
||||
- [americas] Central American producers: Guatemala, Costa Rica, Panama known for balanced profiles #central_america
|
||||
- [americas] Brazilian regions: Cerrado, Sul de Minas, Mogiana with varying profiles #brazil
|
||||
- [asia] Indonesian islands: Sumatra, Java, Sulawesi producing earthy, full-bodied coffees #indonesia
|
||||
- [asia] Vietnamese coffee: World's largest Robusta producer, often used in blends and commercial coffee #vietnam
|
||||
|
||||
## Processing Methods
|
||||
|
||||
- [natural] Beans dried inside the fruit, creating fruity, fermented notes and heavier body #processing
|
||||
- [washed] Fruit removed before drying, resulting in cleaner cup with more pronounced acidity #processing
|
||||
- [honey] Some fruit mucilage left on during drying, creates balanced sweetness and body #processing
|
||||
- [wet-hulled] Unique to Indonesia, creates earthy, herbal, low-acid profiles #processing
|
||||
- [experimental] Anaerobic fermentation, wine-yeast inoculation, and other newer methods #innovation
|
||||
|
||||
## Tasting Notes by Region
|
||||
|
||||
- [ethiopia] Blueberry, jasmine, bergamot, stone fruit, citrus #flavor_notes
|
||||
- [kenya] Blackcurrant, tomato, tropical fruit, wine-like acidity #flavor_notes
|
||||
- [colombia] Caramel, nuts, red apple, chocolate, balanced acidity #flavor_notes
|
||||
- [guatemala] Chocolate, spice, green apple, balanced #flavor_notes
|
||||
- [brazil] Nuts, chocolate, low acidity, full body #flavor_notes
|
||||
- [indonesia] Earthy, herbal, spice, cedar, full body, low acidity #flavor_notes
|
||||
|
||||
## Relations
|
||||
|
||||
- influences [[Flavor Extraction]]
|
||||
- pairs_with [[Coffee Brewing Methods]]
|
||||
- affects [[Tasting Notes]]
|
||||
- relates_to [[Specialty Coffee]]
|
||||
- part_of [[Coffee Knowledge Base]]
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
title: Coffee Brewing Methods
|
||||
type: note
|
||||
permalink: coffee/coffee-brewing-methods
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#brewing'
|
||||
- '#methods'
|
||||
- '#demo'
|
||||
---
|
||||
|
||||
# Coffee Brewing Methods
|
||||
|
||||
An exploration of different coffee brewing techniques, their characteristics, and how they affect flavor extraction.
|
||||
|
||||
## Overview
|
||||
|
||||
Coffee brewing is both an art and a science. Different brewing methods extract different compounds from coffee beans, resulting in unique flavor profiles, body, and mouthfeel. The key variables in any brewing method are:
|
||||
|
||||
- Grind size
|
||||
- Water temperature
|
||||
- Brew time
|
||||
- Coffee-to-water ratio
|
||||
- Agitation/turbulence
|
||||
|
||||
## Observations
|
||||
|
||||
- [principle] Coffee extraction follows a predictable pattern: acids extract first, then sugars, then bitter compounds #extraction
|
||||
- [method] Pour over methods generally produce cleaner, brighter cups with more distinct flavor notes #clarity
|
||||
- [method] Immersion methods like French press create fuller body and more rounded flavors #body
|
||||
- [technique] Water at 195-205°F (90-96°C) extracts optimal flavor compounds for most brewing methods #temperature
|
||||
- [technique] Grind size directly correlates with ideal extraction time (finer = shorter, coarser = longer) #grind
|
||||
- [preference] Medium-light roasts often showcase more origin characteristics in pour over methods #roast
|
||||
- [equipment] Burr grinders produce more consistent particle size than blade grinders, resulting in more even extraction #gear
|
||||
- [ratio] 1:15 to 1:17 coffee-to-water ratio (by weight) works well for most brew methods #brewing
|
||||
- [science] Different brewing temperatures extract different chemical compounds from the beans #chemistry
|
||||
- [technique] Bloom phase (pre-infusion with small amount of water) allows CO2 to escape and improves extraction #bloom
|
||||
|
||||
## Pour Over Methods
|
||||
|
||||
- [method] V60 produces very clean cup with excellent clarity of flavor #pourover
|
||||
- [method] Chemex uses thicker filter paper, resulting in even cleaner cup with fewer oils #pourover
|
||||
- [method] Kalita Wave provides more consistent extraction due to flat bottom design #pourover
|
||||
- [technique] Concentric circular pouring pattern ensures even saturation of grounds #technique
|
||||
- [timing] Most pour over methods complete in 2:30-3:30 total brew time #brewing
|
||||
|
||||
## Immersion Methods
|
||||
|
||||
- [method] French Press creates full-bodied cup with rich mouthfeel due to metal filter allowing oils to pass #immersion
|
||||
- [method] AeroPress is versatile, capable of producing both espresso-like and filter-style coffee #immersion
|
||||
- [method] Cold brew uses time instead of heat to extract, resulting in lower acidity #immersion
|
||||
- [technique] French press ideal steep time is 4-5 minutes before plunging #timing
|
||||
- [technique] AeroPress inverted method prevents dripping during extraction phase #technique
|
||||
|
||||
## Pressure Methods
|
||||
|
||||
- [method] Espresso uses 9 bars of pressure to force water through finely ground coffee #pressure
|
||||
- [method] Moka pot uses steam pressure to push water through grounds, creating strong, concentrated coffee #pressure
|
||||
- [technique] Espresso requires very fine grind, almost powder-like consistency #grind
|
||||
- [timing] Espresso shots typically extract in 25-30 seconds #timing
|
||||
- [principle] Pressure methods can extract compounds that aren't soluble in regular brewing methods #extraction
|
||||
|
||||
## Relations
|
||||
|
||||
- requires [[Proper Grinding Technique]]
|
||||
- affects [[Flavor Extraction]]
|
||||
- pairs_with [[Coffee Bean Origins]]
|
||||
- uses [[Brewing Equipment]]
|
||||
- influences [[Tasting Notes]]
|
||||
- part_of [[Coffee Knowledge Base]]
|
||||
@@ -1,89 +0,0 @@
|
||||
---
|
||||
title: Coffee Flavor Map
|
||||
type: note
|
||||
permalink: coffee/coffee-flavor-map
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#visualization'
|
||||
- '#canvas'
|
||||
- '#demo'
|
||||
---
|
||||
|
||||
# Coffee Flavor Map
|
||||
|
||||
A visual mapping of coffee flavor attributes, brewing methods, and their relationships. This note describes a canvas visualization that could be generated to demonstrate Basic Memory's visualization capabilities.
|
||||
|
||||
## Overview
|
||||
|
||||
The Coffee Flavor Map provides a visual representation of how different brewing methods, coffee origins, and equipment choices affect flavor outcomes. This canvas visualization helps users understand the complex relationships in coffee brewing and tasting.
|
||||
|
||||
## Canvas Visualization Elements
|
||||
|
||||
### Core Nodes
|
||||
- **Flavor Attributes**: Acidity, Sweetness, Body, Clarity, Bitterness, Complexity
|
||||
- **Brewing Methods**: Pour Over, French Press, AeroPress, Espresso, Moka Pot, Cold Brew
|
||||
- **Origin Regions**: Ethiopia, Kenya, Colombia, Brazil, Guatemala, Indonesia
|
||||
- **Equipment Elements**: Grinder Quality, Water Temperature, Brewing Device, Filter Type
|
||||
|
||||
### Node Connections
|
||||
- Lines connecting brewing methods to their typical flavor outcomes
|
||||
- Arrows showing how equipment choices affect extraction variables
|
||||
- Connections between origins and their characteristic flavor profiles
|
||||
- Highlighting of optimal brewing methods for different origins
|
||||
|
||||
### Visual Organization
|
||||
- Flavor outcomes in the center
|
||||
- Brewing methods on the left side
|
||||
- Origins on the right side
|
||||
- Equipment variables at the bottom
|
||||
- Color coding by category (methods, origins, equipment, flavors)
|
||||
|
||||
## Using This Visualization
|
||||
|
||||
### For Coffee Exploration
|
||||
- Identify which brewing methods might highlight the characteristics you prefer
|
||||
- See which origins naturally pair well with your preferred brewing method
|
||||
- Understand how equipment changes can modify flavor outcomes
|
||||
- Visualize the complex interplay between all coffee variables
|
||||
|
||||
### As a Basic Memory Demo
|
||||
- Demonstrates Canvas visualization capabilities
|
||||
- Shows how relations can be visually mapped
|
||||
- Illustrates complex knowledge organization
|
||||
- Provides an intuitive way to navigate coffee knowledge
|
||||
|
||||
## How To Generate This Canvas
|
||||
|
||||
In a conversation with Claude, you could request:
|
||||
|
||||
```
|
||||
Please create a canvas visualization mapping the relationships between coffee brewing methods, origins, and flavor outcomes. Show how different equipment and techniques influence extraction and resulting flavor profiles.
|
||||
```
|
||||
|
||||
This would generate a `.canvas` file in your Basic Memory directory that could be opened with Obsidian for an interactive visualization of these coffee relationships.
|
||||
|
||||
## Example Visualization Snippets
|
||||
|
||||
### Pour Over Method Node
|
||||
- Connected to: High Clarity, Bright Acidity, Medium Body
|
||||
- Best pairs with: Ethiopian and Kenyan beans
|
||||
- Equipment dependencies: Gooseneck Kettle, Paper Filter, Burr Grinder
|
||||
|
||||
### Ethiopian Coffee Node
|
||||
- Characteristic flavors: Floral, Fruity, Bright
|
||||
- Best brewing methods: Pour Over, AeroPress
|
||||
- Challenging with: French Press (loses clarity of delicate notes)
|
||||
|
||||
### Grind Size Node
|
||||
- Affects: Extraction Rate, Flavor Balance
|
||||
- Fine grind increases: Extraction Speed, Surface Area
|
||||
- Coarse grind increases: Flow Rate, Reduces Bitter Compounds
|
||||
|
||||
## Relations
|
||||
|
||||
- visualizes [[Coffee Knowledge Base]]
|
||||
- relates_to [[Coffee Brewing Methods]]
|
||||
- relates_to [[Coffee Bean Origins]]
|
||||
- relates_to [[Flavor Extraction]]
|
||||
- relates_to [[Tasting Notes]]
|
||||
- demonstrates [[Canvas]]
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
title: Coffee Knowledge Base
|
||||
type: note
|
||||
permalink: coffee/coffee-knowledge-base
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#index'
|
||||
- '#demo'
|
||||
- '#knowledge'
|
||||
---
|
||||
|
||||
# Coffee Knowledge Base
|
||||
|
||||
A comprehensive collection of coffee knowledge, from bean origins to brewing methods to tasting notes. This knowledge base demonstrates Basic Memory's ability to organize and connect information in a meaningful way.
|
||||
|
||||
## Overview
|
||||
|
||||
This Coffee Knowledge Base captures key information about coffee, structured with semantic observations and relations that connect different aspects of coffee knowledge. It serves as both a useful reference for coffee enthusiasts and a demonstration of how Basic Memory organizes information.
|
||||
|
||||
## Key Topics
|
||||
|
||||
### Core Coffee Knowledge
|
||||
|
||||
- [[Coffee Brewing Methods]] - Different techniques for preparing coffee
|
||||
- [[Coffee Bean Origins]] - Where coffee comes from and how region affects flavor
|
||||
- [[Brewing Equipment]] - Tools and devices used to prepare coffee
|
||||
- [[Flavor Extraction]] - The science of dissolving flavor compounds from coffee
|
||||
- [[Tasting Notes]] - How to taste and describe coffee flavors
|
||||
|
||||
### Brewing Techniques
|
||||
|
||||
- Proper grinding is fundamental to good extraction
|
||||
- Water quality significantly impacts flavor
|
||||
- Different brewing methods highlight different characteristics
|
||||
- Time, temperature, and grind size are the key variables to control
|
||||
- Freshness of beans dramatically affects quality
|
||||
|
||||
### Coffee Preferences
|
||||
|
||||
- Light roasts preserve more origin characteristics and acidity
|
||||
- Dark roasts emphasize body and chocolatey/roasted flavors
|
||||
- Pour over methods highlight clarity and distinct flavor notes
|
||||
- Immersion methods create fuller body and rounded flavor
|
||||
- Personal preference matters more than "correctness"
|
||||
|
||||
## Using This Knowledge Base
|
||||
|
||||
### For Learning
|
||||
|
||||
Use this knowledge base to:
|
||||
- Understand coffee fundamentals
|
||||
- Explore connections between brewing methods and flavor outcomes
|
||||
- Learn how different origins produce distinct flavor profiles
|
||||
- Discover how equipment affects the brewing process
|
||||
- Develop a vocabulary for describing coffee experiences
|
||||
|
||||
### As a Demo
|
||||
|
||||
This knowledge base demonstrates:
|
||||
- Semantic knowledge organization with categories and relations
|
||||
- Building connections between related concepts
|
||||
- Creating a navigable knowledge graph
|
||||
- Structuring information in a way both humans and AI assistants can understand
|
||||
- How Basic Memory enables persistent knowledge across conversations
|
||||
|
||||
## Relations
|
||||
|
||||
- contains [[Coffee Brewing Methods]]
|
||||
- contains [[Coffee Bean Origins]]
|
||||
- contains [[Brewing Equipment]]
|
||||
- contains [[Flavor Extraction]]
|
||||
- contains [[Tasting Notes]]
|
||||
- demonstrates [[Basic Memory Capabilities]]
|
||||
@@ -1,79 +0,0 @@
|
||||
---
|
||||
title: Flavor Extraction
|
||||
type: note
|
||||
permalink: coffee/flavor-extraction
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#extraction'
|
||||
- '#brewing'
|
||||
- '#science'
|
||||
- '#demo'
|
||||
---
|
||||
|
||||
# Flavor Extraction
|
||||
|
||||
Understanding the science of coffee extraction, how different compounds dissolve at different rates, and how to control extraction to achieve desired flavor profiles.
|
||||
|
||||
## Overview
|
||||
|
||||
Coffee extraction is the process of dissolving flavor compounds from ground coffee into water. The science of extraction is key to producing a balanced, flavorful cup. Extraction is affected by numerous variables including grind size, water temperature, contact time, agitation, and pressure.
|
||||
|
||||
## Observations
|
||||
|
||||
- [science] Coffee contains over 1,000 aroma compounds and hundreds of flavor compounds #chemistry
|
||||
- [principle] Extraction occurs in a predictable sequence: acids → sugars → bitter compounds #extraction_order
|
||||
- [principle] Under-extraction results in sour, bright, thin coffee lacking sweetness and body #under_extraction
|
||||
- [principle] Over-extraction results in bitter, hollow, astringent flavors #over_extraction
|
||||
- [principle] The goal is typically balanced extraction (18-22% of coffee solubles dissolved) #balanced_extraction
|
||||
- [technique] Finer grind size increases extraction rate due to greater surface area #grind_size
|
||||
- [technique] Higher water temperature increases extraction rate and solubility of compounds #temperature
|
||||
- [technique] Longer contact time allows more complete extraction #brew_time
|
||||
- [technique] Agitation (stirring, turbulence) increases extraction rate by preventing saturation zones #agitation
|
||||
- [technique] Pressure (as in espresso) can extract compounds that aren't water-soluble at atmospheric pressure #pressure
|
||||
|
||||
## Factors Affecting Extraction
|
||||
|
||||
- [factor] Grind size: Finer = faster extraction, coarser = slower extraction #grind
|
||||
- [factor] Water temperature: Higher = faster extraction, lower = slower extraction #temperature
|
||||
- [factor] Contact time: Longer = more extraction, shorter = less extraction #time
|
||||
- [factor] Agitation: More = faster extraction, less = slower extraction #agitation
|
||||
- [factor] Coffee-to-water ratio: More coffee = lower extraction percentage #ratio
|
||||
- [factor] Water quality: Mineral content affects extraction of different compounds #water
|
||||
- [factor] Roast level: Darker roasts extract more easily than lighter roasts #roast
|
||||
- [factor] Bean density: Denser beans (typically high-altitude) require more effort to extract #density
|
||||
- [factor] Freshness: Freshly roasted coffee extracts differently than aged coffee #freshness
|
||||
- [factor] Brewing method: Different methods extract different compounds at different rates #method
|
||||
|
||||
## Signs of Extraction Levels
|
||||
|
||||
- [under] Sour, bright, lack of sweetness, thin body, quick finish #flavor
|
||||
- [under] Typically from: too coarse grind, too cool water, too short brew time #causes
|
||||
- [balanced] Sweet, bright but not sour, rich but not bitter, pleasing finish #flavor
|
||||
- [balanced] Achieved through proper ratio of variables for given coffee #technique
|
||||
- [over] Bitter, hollow, astringent, dry finish, sometimes papery #flavor
|
||||
- [over] Typically from: too fine grind, too hot water, too long brew time #causes
|
||||
|
||||
## Measuring Extraction
|
||||
|
||||
- [method] Total Dissolved Solids (TDS) meters measure concentration of coffee solution #measurement
|
||||
- [method] Extraction yield = percentage of coffee grounds dissolved in the final brew #calculation
|
||||
- [preference] Specialty coffee typically targets 18-22% extraction yield #standards
|
||||
- [preference] Some specialty light roasts may taste best at higher extraction percentages #speciality
|
||||
|
||||
## Controlling Extraction
|
||||
|
||||
- [technique] Adjust grind size as primary extraction control #basics
|
||||
- [technique] Use water temperature to fine-tune extraction #fine_tuning
|
||||
- [technique] Modify pour technique to control agitation level #technique
|
||||
- [technique] Adjust coffee-to-water ratio to balance strength and extraction #ratio
|
||||
- [technique] Pre-infusion (blooming) helps achieve even extraction #blooming
|
||||
- [technique] Pulse pouring creates different extraction dynamics than continuous pour #pour_technique
|
||||
|
||||
## Relations
|
||||
|
||||
- affected_by [[Coffee Brewing Methods]]
|
||||
- influenced_by [[Coffee Bean Origins]]
|
||||
- enhanced_by [[Brewing Equipment]]
|
||||
- determines [[Tasting Notes]]
|
||||
- requires [[Water Quality]]
|
||||
- part_of [[Coffee Knowledge Base]]
|
||||
@@ -1,161 +0,0 @@
|
||||
{
|
||||
"nodes":[
|
||||
{
|
||||
"id":"node-5",
|
||||
"type":"text",
|
||||
"text":"## Main Pour Phase\n- Use concentric circles from center outward\n- Maintain steady, controlled flow rate\n- Avoid pouring directly on filter walls\n- Keep water level consistent\n- Pulse pour in 2-3 stages (or continuous pour)\n- Total brew time target: 2:30-3:30",
|
||||
"position":{"x":450,"y":200},
|
||||
"x":530,
|
||||
"y":-100,
|
||||
"width":300,
|
||||
"height":200,
|
||||
"color":"1"
|
||||
},
|
||||
{
|
||||
"id":"node-8",
|
||||
"type":"text",
|
||||
"text":"## Drawdown\n- Allow water to fully drain\n- Flat bed indicates even extraction\n- Total brew time should be ~2:30-3:30\n- Remove filter promptly after brewing",
|
||||
"position":{"x":450,"y":700},
|
||||
"x":540,
|
||||
"y":375,
|
||||
"width":300,
|
||||
"height":150,
|
||||
"color":"1"
|
||||
},
|
||||
{
|
||||
"id":"node-6",
|
||||
"type":"text",
|
||||
"text":"## Pour Pattern\n\nConcentric circles ensure even saturation of coffee grounds. Begin at the center and work outward, avoiding filter edges. Pour height of 1-2 inches above coffee bed.",
|
||||
"position":{"x":250,"y":450},
|
||||
"x":960,
|
||||
"y":25,
|
||||
"width":300,
|
||||
"height":150,
|
||||
"color":"5"
|
||||
},
|
||||
{
|
||||
"id":"node-12",
|
||||
"type":"text",
|
||||
"text":"## Tasting Notes\n\n- Balanced extraction: sweet, bright, complex\n- Under-extraction: sour, lacking sweetness\n- Over-extraction: bitter, astringent, hollow\n\nTake notes on each brew to track improvements and preferences.",
|
||||
"position":{"x":-250,"y":700},
|
||||
"x":1020,
|
||||
"y":420,
|
||||
"width":300,
|
||||
"height":150,
|
||||
"color":"6"
|
||||
},
|
||||
{
|
||||
"id":"node-9",
|
||||
"type":"text",
|
||||
"text":"## Troubleshooting\n\n- Too sour/weak: Grind finer, water hotter, increase brew time\n- Too bitter/strong: Grind coarser, water cooler, decrease brew time\n- Uneven extraction: Improve pour technique, better grinder\n- Channeling: More careful pouring, better bloom\n- Slow drawdown: Coarser grind, less agitation\n- Fast drawdown: Finer grind, more careful pouring",
|
||||
"position":{"x":100,"y":700},
|
||||
"x":30,
|
||||
"y":570,
|
||||
"width":300,
|
||||
"height":200,
|
||||
"color":"6"
|
||||
},
|
||||
{
|
||||
"id":"node-3",
|
||||
"type":"text",
|
||||
"text":"## Preparation\n- Heat water to 195-205°F (90-96°C)\n- Measure coffee (1:15 to 1:17 ratio)\n- Medium-fine grind (sea salt consistency)\n- Rinse filter with hot water\n- Discard rinse water\n- Add ground coffee to filter\n- Level coffee bed",
|
||||
"position":{"x":-250,"y":200},
|
||||
"x":30,
|
||||
"y":-500,
|
||||
"width":300,
|
||||
"height":200,
|
||||
"color":"3"
|
||||
},
|
||||
{
|
||||
"id":"node-1",
|
||||
"type":"text",
|
||||
"text":"# Perfect Pour Over Method\n\nA systematic approach to brewing exceptional pour over coffee by controlling key variables and following proper technique.",
|
||||
"position":{"x":0,"y":0},
|
||||
"x":-580,
|
||||
"y":-760,
|
||||
"width":400,
|
||||
"height":120,
|
||||
"color":"4"
|
||||
},
|
||||
{
|
||||
"id":"node-10",
|
||||
"type":"text",
|
||||
"text":"## Grinding Parameters\n\n- V60: Medium-fine (sea salt)\n- Chemex: Medium (slightly coarser than V60)\n- Kalita Wave: Medium (between V60 and Chemex)\n\nConsistent particle size is critical; use quality burr grinder.",
|
||||
"position":{"x":-250,"y":450},
|
||||
"x":30,
|
||||
"y":-910,
|
||||
"width":300,
|
||||
"height":150,
|
||||
"color":"5"
|
||||
},
|
||||
{
|
||||
"id":"node-2",
|
||||
"type":"text",
|
||||
"text":"## Equipment Setup\n- Clean V60/Chemex/Kalita Wave\n- Paper filter (rinsed)\n- Server/mug\n- Scale with timer\n- Gooseneck kettle\n- Burr grinder\n- Fresh coffee beans",
|
||||
"position":{"x":-600,"y":200},
|
||||
"x":-530,
|
||||
"y":-500,
|
||||
"width":300,
|
||||
"height":200,
|
||||
"color":"3"
|
||||
},
|
||||
{
|
||||
"id":"node-4",
|
||||
"type":"text",
|
||||
"text":"## The Bloom\n- Start timer\n- Pour 2-3x coffee weight water\n- Ensure all grounds are saturated\n- Gentle stir or swirl if needed\n- Allow 30-45 seconds for degassing\n- Look for bubbling and dome formation",
|
||||
"position":{"x":100,"y":200},
|
||||
"x":530,
|
||||
"y":-500,
|
||||
"width":300,
|
||||
"height":200,
|
||||
"color":"1"
|
||||
},
|
||||
{
|
||||
"id":"node-13",
|
||||
"type":"text",
|
||||
"text":"## Coffee-to-Water Ratio\n\n- Standard: 1:15 to 1:17 (coffee:water)\n- Stronger cup: 1:15 (67g/L)\n- Medium cup: 1:16 (62.5g/L)\n- Lighter cup: 1:17 (58.8g/L)\n\nExample: For 300ml water, use ~18-20g coffee",
|
||||
"position":{"x":-600,"y":700},
|
||||
"x":30,
|
||||
"y":-100,
|
||||
"width":300,
|
||||
"height":150,
|
||||
"color":"6"
|
||||
},
|
||||
{
|
||||
"id":"node-7",
|
||||
"type":"text",
|
||||
"text":"## Brew Time Guideline\n\n- Bloom: 30-45 seconds\n- First pour: 1:00-1:15\n- Second pour: 1:45-2:00\n- Final pour: 2:15-2:30\n- Drawdown complete: 2:45-3:30\n\nAdjust for taste: shorter for lighter, longer for stronger",
|
||||
"position":{"x":600,"y":450},
|
||||
"x":-80,
|
||||
"y":220,
|
||||
"width":300,
|
||||
"height":200,
|
||||
"color":"5"
|
||||
},
|
||||
{
|
||||
"id":"node-11",
|
||||
"type":"text",
|
||||
"text":"## Water Quality\n\n- Clean, filtered water\n- No strong odors or flavors\n- Ideal TDS: 75-150 ppm\n- Ideal pH: 7.0-7.5\n- Avoid distilled water (lacks minerals)\n- Avoid hard water (scaling issues)",
|
||||
"position":{"x":-600,"y":450},
|
||||
"x":-780,
|
||||
"y":-125,
|
||||
"width":300,
|
||||
"height":150,
|
||||
"color":"5"
|
||||
}
|
||||
],
|
||||
"edges":[
|
||||
{"id":"edge-1","fromNode":"node-1","fromSide":"bottom","toNode":"node-2","toSide":"top","label":"Step 1"},
|
||||
{"id":"edge-2","fromNode":"node-2","fromSide":"right","toNode":"node-3","toSide":"left","label":"Step 2"},
|
||||
{"id":"edge-3","fromNode":"node-3","fromSide":"right","toNode":"node-4","toSide":"left","label":"Step 3"},
|
||||
{"id":"edge-4","fromNode":"node-4","fromSide":"bottom","toNode":"node-5","toSide":"top","label":"Step 4"},
|
||||
{"id":"edge-5","fromNode":"node-5","fromSide":"bottom","toNode":"node-8","toSide":"top","label":"Step 5"},
|
||||
{"id":"edge-6","fromNode":"node-5","fromSide":"left","toNode":"node-7","toSide":"right","label":"Timing"},
|
||||
{"id":"edge-7","fromNode":"node-5","fromSide":"right","toNode":"node-6","toSide":"left","label":"Technique"},
|
||||
{"id":"edge-8","fromNode":"node-8","fromSide":"left","toNode":"node-9","toSide":"right","label":"if problems"},
|
||||
{"id":"edge-9","fromNode":"node-3","fromSide":"top","toNode":"node-10","toSide":"bottom","label":"Grinding details"},
|
||||
{"id":"edge-10","fromNode":"node-2","fromSide":"bottom","toNode":"node-11","toSide":"right","label":"Water details"},
|
||||
{"id":"edge-11","fromNode":"node-8","fromSide":"right","toNode":"node-12","toSide":"left","label":"Evaluate"},
|
||||
{"id":"edge-12","fromNode":"node-3","fromSide":"bottom","toNode":"node-13","toSide":"top","label":"Ratio details"}
|
||||
]
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
---
|
||||
title: Tasting Notes
|
||||
type: note
|
||||
permalink: coffee/tasting-notes
|
||||
tags:
|
||||
- '#coffee'
|
||||
- '#tasting'
|
||||
- '#flavor'
|
||||
- '#cupping'
|
||||
- '#demo'
|
||||
---
|
||||
|
||||
# Tasting Notes
|
||||
|
||||
How to taste and evaluate coffee, identify flavor characteristics, and develop a personal coffee palate.
|
||||
|
||||
## Overview
|
||||
|
||||
Coffee tasting, or "cupping" in professional contexts, is the practice of observing the tastes and aromas of brewed coffee. Developing a coffee palate helps identify preferences, communicate about coffee experiences, and better understand how brewing variables affect the cup.
|
||||
|
||||
## Observations
|
||||
|
||||
- [principle] Flavor perception includes taste, aroma, mouthfeel, and retronasal perception #sensory
|
||||
- [principle] Our taste buds can only perceive sweet, sour, salty, bitter, and umami #taste
|
||||
- [principle] Most of what we call "flavor" is actually aroma detected retronasally #aroma
|
||||
- [technique] Professional coffee tasting (cupping) uses a standardized protocol for consistency #cupping
|
||||
- [technique] Slurping coffee aerates it and spreads it across all taste receptors #technique
|
||||
- [technique] Allowing coffee to cool reveals different flavor notes at different temperatures #temperature
|
||||
|
||||
## Coffee Flavor Wheel
|
||||
|
||||
- [tool] The SCA Coffee Flavor Wheel provides a standardized vocabulary for describing coffee #flavor_wheel
|
||||
- [category] Primary categories include: Fruity, Floral, Sweet, Nutty/Cocoa, Spice, Roasted, Other #categories
|
||||
- [subcategory] Fruity breaks down into: Berry, Dried Fruit, Citrus Fruit, Stone Fruit, Tropical Fruit, etc. #fruit_notes
|
||||
- [subcategory] Floral includes: Floral, Black Tea, Chamomile, Rose, Jasmine, etc. #floral_notes
|
||||
- [subcategory] Sweet includes: Brown Sugar, Molasses, Honey, Maple Syrup, Vanilla, etc. #sweet_notes
|
||||
- [subcategory] Nutty/Cocoa includes: Nut, Cocoa, Dark Chocolate, Chocolate, etc. #nutty_notes
|
||||
- [subcategory] Spice includes: Brown Spice, Pepper, Anise, Nutmeg, Cinnamon, etc. #spice_notes
|
||||
|
||||
## Basic Tasting Components
|
||||
|
||||
- [component] Acidity: The bright, tangy quality (not sourness from under-extraction) #acidity
|
||||
- [component] Sweetness: The pleasant, sugary quality balancing other elements #sweetness
|
||||
- [component] Body: The physical mouthfeel and weight of the coffee #body
|
||||
- [component] Finish/Aftertaste: The flavor that lingers after swallowing #finish
|
||||
- [component] Balance: How well all elements work together #balance
|
||||
- [component] Complexity: The range and layers of distinct flavors #complexity
|
||||
- [component] Cleanliness: Absence of defects or off-flavors #cleanliness
|
||||
|
||||
## Common Flavor Notes by Origin
|
||||
|
||||
- [ethiopia] Blueberry, jasmine, bergamot, lemon, tea-like #flavor_notes
|
||||
- [kenya] Blackcurrant, grapefruit, tomato-like acidity, winey #flavor_notes
|
||||
- [colombia] Caramel, red apple, nuts, chocolate, balanced acidity #flavor_notes
|
||||
- [guatemala] Chocolate, spice, apple, medium acidity #flavor_notes
|
||||
- [brazil] Nuts, chocolate, low-to-medium acidity, full body #flavor_notes
|
||||
- [indonesia] Earthy, herbal, spice, cedar, full body, low acidity #flavor_notes
|
||||
- [costa_rica] Clean, bright, citrus, balanced, light chocolate #flavor_notes
|
||||
|
||||
## Developing Your Palate
|
||||
|
||||
- [technique] Taste coffees side-by-side to identify differences #comparison
|
||||
- [technique] Try describing flavors before looking at roaster's notes #blind_tasting
|
||||
- [technique] Keep a coffee journal with detailed notes about each coffee #journaling
|
||||
- [technique] Explore different processing methods of the same origin #processing
|
||||
- [technique] Try the same coffee brewed with different methods #brewing_comparison
|
||||
- [technique] Use reference flavors (actual fruits, chocolates, etc.) to calibrate your palate #calibration
|
||||
|
||||
## Personal Coffee Experiences
|
||||
|
||||
- [experience] Ethiopian Yirgacheffe prepared as pour over: intense blueberry, jasmine aromatics, tea-like body
|
||||
- [experience] Sumatra Mandheling in French press: earthy, cedar, herbal, tobacco, full body
|
||||
- [experience] Panama Gesha as pour over: intense floral notes, jasmine, bergamot, delicate body
|
||||
- [experience] Brazil Cerrado as espresso: nutty, chocolate, caramel, low acidity, great crema
|
||||
- [experience] Kenya AA as pour over: bright blackcurrant, tomato-like acidity, winey finish
|
||||
|
||||
## Relations
|
||||
|
||||
- determined_by [[Flavor Extraction]]
|
||||
- influenced_by [[Coffee Bean Origins]]
|
||||
- varies_with [[Coffee Brewing Methods]]
|
||||
- enhanced_by [[Proper Grinding Technique]]
|
||||
- documented_in [[Coffee Journal]]
|
||||
- part_of [[Coffee Knowledge Base]]
|
||||
@@ -1,69 +0,0 @@
|
||||
---
|
||||
title: Test Note Creation - Basic Functionality
|
||||
type: note
|
||||
permalink: testing/test-note-creation-basic-functionality
|
||||
tags:
|
||||
- '["testing"'
|
||||
- '"core-functionality"'
|
||||
- '"note-creation"]'
|
||||
---
|
||||
|
||||
---
|
||||
title: Test Note Creation - Basic Functionality
|
||||
tags: [testing, core-functionality, note-creation, edited]
|
||||
test_status: active
|
||||
last_edited: 2025-06-01
|
||||
---
|
||||
|
||||
# Test Note Creation - Basic Functionality
|
||||
|
||||
## Test Status: COMPREHENSIVE TESTING IN PROGRESS
|
||||
Testing basic note creation with various content types and structures.
|
||||
|
||||
## Content Types Tested
|
||||
- Plain text content ✓
|
||||
- Markdown formatting **bold**, *italic*
|
||||
- Lists:
|
||||
- Bullet points
|
||||
- Numbered items
|
||||
- Code blocks: `inline code`
|
||||
|
||||
```python
|
||||
# Block code
|
||||
def test_function():
|
||||
return "Hello, Basic Memory!"
|
||||
```
|
||||
|
||||
## Special Characters
|
||||
- Unicode: café, naïve, résumé
|
||||
- Emojis: 🚀 🔬 📝
|
||||
- Symbols: @#$%^&*()
|
||||
|
||||
## Frontmatter Testing
|
||||
This note should have proper frontmatter parsing.
|
||||
|
||||
## Relations to Test
|
||||
- connects_to [[Another Test Note]]
|
||||
- validates [[Core Functionality Tests]]
|
||||
|
||||
## Observations
|
||||
- [success] Note creation initiated
|
||||
- [test] Content variety included
|
||||
- [validation] Special characters included
|
||||
|
||||
|
||||
## Edit Test Results
|
||||
- [success] Note reading via title lookup ✓
|
||||
- [success] Search functionality returns relevant results ✓
|
||||
- [success] Special characters (unicode, emojis) preserved ✓
|
||||
- [test] Now testing append edit operation ✓
|
||||
|
||||
## Performance Notes
|
||||
- Note creation: Instantaneous
|
||||
- Note reading: Fast response
|
||||
- Search: Good relevance scoring
|
||||
|
||||
## Next Tests
|
||||
- Edit operations (append, prepend, find_replace)
|
||||
- Move operations
|
||||
- Cross-project functionality
|
||||
@@ -2,25 +2,105 @@
|
||||
|
||||
# Install dependencies
|
||||
install:
|
||||
pip install -e ".[dev]"
|
||||
uv pip install -e ".[dev]"
|
||||
uv sync
|
||||
@echo ""
|
||||
@echo "💡 Remember to activate the virtual environment by running: source .venv/bin/activate"
|
||||
|
||||
# Run unit tests in parallel
|
||||
test-unit:
|
||||
uv run pytest -p pytest_mock -v -n auto
|
||||
# ==============================================================================
|
||||
# DATABASE BACKEND TESTING
|
||||
# ==============================================================================
|
||||
# Basic Memory supports dual database backends (SQLite and Postgres).
|
||||
# By default, tests run against SQLite (fast, no dependencies).
|
||||
# Set BASIC_MEMORY_TEST_POSTGRES=1 to run against Postgres (uses testcontainers).
|
||||
#
|
||||
# Quick Start:
|
||||
# just test # Run all tests against SQLite (default)
|
||||
# just test-sqlite # Run all tests against SQLite
|
||||
# just test-postgres # Run all tests against Postgres (testcontainers)
|
||||
# just test-unit-sqlite # Run unit tests against SQLite
|
||||
# just test-unit-postgres # Run unit tests against Postgres
|
||||
# just test-int-sqlite # Run integration tests against SQLite
|
||||
# just test-int-postgres # Run integration tests against Postgres
|
||||
#
|
||||
# CI runs both in parallel for faster feedback.
|
||||
# ==============================================================================
|
||||
|
||||
# Run integration tests in parallel
|
||||
test-int:
|
||||
uv run pytest -p pytest_mock -v --no-cov -n auto test-int
|
||||
# Run all tests against SQLite and Postgres
|
||||
test: test-sqlite test-postgres
|
||||
|
||||
# Run all tests
|
||||
test: test-unit test-int
|
||||
# Run all tests against SQLite
|
||||
test-sqlite: test-unit-sqlite test-int-sqlite
|
||||
|
||||
# Run all tests against Postgres (uses testcontainers)
|
||||
test-postgres: test-unit-postgres test-int-postgres
|
||||
|
||||
# Run unit tests against SQLite
|
||||
test-unit-sqlite:
|
||||
uv run pytest -p pytest_mock -v --no-cov tests
|
||||
|
||||
# Run unit tests against Postgres
|
||||
test-unit-postgres:
|
||||
BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov tests
|
||||
|
||||
# Run integration tests against SQLite
|
||||
test-int-sqlite:
|
||||
uv run pytest -p pytest_mock -v --no-cov test-int
|
||||
|
||||
# Run integration tests against Postgres
|
||||
# Note: Uses timeout due to FastMCP Client + asyncpg cleanup hang (tests pass, process hangs on exit)
|
||||
# See: https://github.com/jlowin/fastmcp/issues/1311
|
||||
test-int-postgres:
|
||||
timeout --signal=KILL 600 bash -c 'BASIC_MEMORY_TEST_POSTGRES=1 uv run pytest -p pytest_mock -v --no-cov test-int' || test $? -eq 137
|
||||
|
||||
# Reset Postgres test database (drops and recreates schema)
|
||||
# Useful when Alembic migration state gets out of sync during development
|
||||
# Uses credentials from docker-compose-postgres.yml
|
||||
postgres-reset:
|
||||
docker exec basic-memory-postgres psql -U ${POSTGRES_USER:-basic_memory_user} -d ${POSTGRES_TEST_DB:-basic_memory_test} -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
|
||||
@echo "✅ Postgres test database reset"
|
||||
|
||||
# Run Alembic migrations manually against Postgres test database
|
||||
# Useful for debugging migration issues
|
||||
# Uses credentials from docker-compose-postgres.yml (can override with env vars)
|
||||
postgres-migrate:
|
||||
@cd src/basic_memory/alembic && \
|
||||
BASIC_MEMORY_DATABASE_BACKEND=postgres \
|
||||
BASIC_MEMORY_DATABASE_URL=${POSTGRES_TEST_URL:-postgresql+asyncpg://basic_memory_user:dev_password@localhost:5433/basic_memory_test} \
|
||||
uv run alembic upgrade head
|
||||
@echo "✅ Migrations applied to Postgres test database"
|
||||
|
||||
# Run Windows-specific tests only (only works on Windows platform)
|
||||
# These tests verify Windows-specific database optimizations (locking mode, NullPool)
|
||||
# Will be skipped automatically on non-Windows platforms
|
||||
test-windows:
|
||||
uv run pytest -p pytest_mock -v --no-cov -m windows tests test-int
|
||||
|
||||
# Run benchmark tests only (performance testing)
|
||||
# These are slow tests that measure sync performance with various file counts
|
||||
# Excluded from default test runs to keep CI fast
|
||||
test-benchmark:
|
||||
uv run pytest -p pytest_mock -v --no-cov -m benchmark tests test-int
|
||||
|
||||
# Run all tests including Windows, Postgres, and Benchmarks (for CI/comprehensive testing)
|
||||
# Use this before releasing to ensure everything works across all backends and platforms
|
||||
test-all:
|
||||
uv run pytest -p pytest_mock -v --no-cov tests test-int
|
||||
|
||||
# Generate HTML coverage report
|
||||
coverage:
|
||||
uv run pytest -p pytest_mock -v -n auto tests test-int --cov-report=html
|
||||
@echo "Coverage report generated in htmlcov/index.html"
|
||||
|
||||
# Lint and fix code (calls fix)
|
||||
lint: fix
|
||||
|
||||
# Lint and fix code
|
||||
lint:
|
||||
ruff check . --fix
|
||||
fix:
|
||||
uv run ruff check --fix --unsafe-fixes src tests test-int
|
||||
|
||||
# Type check code
|
||||
type-check:
|
||||
typecheck:
|
||||
uv run pyright
|
||||
|
||||
# Clean build artifacts and cache files
|
||||
@@ -52,12 +132,131 @@ update-deps:
|
||||
uv sync --upgrade
|
||||
|
||||
# Run all code quality checks and tests
|
||||
check: lint format type-check test
|
||||
check: lint format typecheck test
|
||||
|
||||
# Generate Alembic migration with descriptive message
|
||||
migration message:
|
||||
cd src/basic_memory/alembic && alembic revision --autogenerate -m "{{message}}"
|
||||
|
||||
# Create a stable release (e.g., just release v0.13.2)
|
||||
release version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Validate version format
|
||||
if [[ ! "{{version}}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "❌ Invalid version format. Use: v0.13.2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version number without 'v' prefix
|
||||
VERSION_NUM=$(echo "{{version}}" | sed 's/^v//')
|
||||
|
||||
echo "🚀 Creating stable release {{version}}"
|
||||
|
||||
# Pre-flight checks
|
||||
echo "📋 Running pre-flight checks..."
|
||||
if [[ -n $(git status --porcelain) ]]; then
|
||||
echo "❌ Uncommitted changes found. Please commit or stash them first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $(git branch --show-current) != "main" ]]; then
|
||||
echo "❌ Not on main branch. Switch to main first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if tag already exists
|
||||
if git tag -l "{{version}}" | grep -q "{{version}}"; then
|
||||
echo "❌ Tag {{version}} already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run quality checks
|
||||
echo "🔍 Running quality checks..."
|
||||
just check
|
||||
|
||||
# Update version in __init__.py
|
||||
echo "📝 Updating version in __init__.py..."
|
||||
sed -i.bak "s/__version__ = \".*\"/__version__ = \"$VERSION_NUM\"/" src/basic_memory/__init__.py
|
||||
rm -f src/basic_memory/__init__.py.bak
|
||||
|
||||
# Commit version update
|
||||
git add src/basic_memory/__init__.py
|
||||
git commit -m "chore: update version to $VERSION_NUM for {{version}} release"
|
||||
|
||||
# Create and push tag
|
||||
echo "🏷️ Creating tag {{version}}..."
|
||||
git tag "{{version}}"
|
||||
|
||||
echo "📤 Pushing to GitHub..."
|
||||
git push origin main
|
||||
git push origin "{{version}}"
|
||||
|
||||
echo "✅ Release {{version}} created successfully!"
|
||||
echo "📦 GitHub Actions will build and publish to PyPI"
|
||||
echo "🔗 Monitor at: https://github.com/basicmachines-co/basic-memory/actions"
|
||||
|
||||
# Create a beta release (e.g., just beta v0.13.2b1)
|
||||
beta version:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Validate version format (allow beta/rc suffixes)
|
||||
if [[ ! "{{version}}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(b[0-9]+|rc[0-9]+)$ ]]; then
|
||||
echo "❌ Invalid beta version format. Use: v0.13.2b1 or v0.13.2rc1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version number without 'v' prefix
|
||||
VERSION_NUM=$(echo "{{version}}" | sed 's/^v//')
|
||||
|
||||
echo "🧪 Creating beta release {{version}}"
|
||||
|
||||
# Pre-flight checks
|
||||
echo "📋 Running pre-flight checks..."
|
||||
if [[ -n $(git status --porcelain) ]]; then
|
||||
echo "❌ Uncommitted changes found. Please commit or stash them first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $(git branch --show-current) != "main" ]]; then
|
||||
echo "❌ Not on main branch. Switch to main first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if tag already exists
|
||||
if git tag -l "{{version}}" | grep -q "{{version}}"; then
|
||||
echo "❌ Tag {{version}} already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run quality checks
|
||||
echo "🔍 Running quality checks..."
|
||||
just check
|
||||
|
||||
# Update version in __init__.py
|
||||
echo "📝 Updating version in __init__.py..."
|
||||
sed -i.bak "s/__version__ = \".*\"/__version__ = \"$VERSION_NUM\"/" src/basic_memory/__init__.py
|
||||
rm -f src/basic_memory/__init__.py.bak
|
||||
|
||||
# Commit version update
|
||||
git add src/basic_memory/__init__.py
|
||||
git commit -m "chore: update version to $VERSION_NUM for {{version}} beta release"
|
||||
|
||||
# Create and push tag
|
||||
echo "🏷️ Creating tag {{version}}..."
|
||||
git tag "{{version}}"
|
||||
|
||||
echo "📤 Pushing to GitHub..."
|
||||
git push origin main
|
||||
git push origin "{{version}}"
|
||||
|
||||
echo "✅ Beta release {{version}} created successfully!"
|
||||
echo "📦 GitHub Actions will build and publish to PyPI as pre-release"
|
||||
echo "🔗 Monitor at: https://github.com/basicmachines-co/basic-memory/actions"
|
||||
echo "📥 Install with: uv tool install basic-memory --pre"
|
||||
|
||||
# List all available recipes
|
||||
default:
|
||||
@just --list
|
||||
@just --list
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
{"type":"entity","name":"Paul","entityType":"person","observations":["Software developer combining DIY ethics, Free Software principles, and theoretical computer science","Created the Basic Machines project","Values authentic exchange of ideas","Approaches AI interaction with emphasis on genuine technical discussion","Comfortable with uncertainty and open dialogue","Balances practical implementation with broader implications"]}
|
||||
{"type":"entity","name":"Basic_Machines","entityType":"project","observations":["Local-first knowledge management system","Combines filesystem durability with graph-based knowledge representation","Focuses on enhancing human agency and understanding","Synthesizes DIY ethics, Free Software philosophy, and theoretical computer science","Current focus includes basic-memory system"]}
|
||||
{"type":"entity","name":"basic-memory","entityType":"software_system","observations":["A core component of Basic Machines","Local-first knowledge management system","Combines filesystem persistence with graph-based knowledge representation","Being implemented collaboratively by Paul and Claude"]}
|
||||
{"type":"entity","name":"basic-memory_implementation_patterns","entityType":"technical_patterns","observations":["Filesystem is source of truth - all changes write to files first","Clean separation of concerns between models (SQLAlchemy), schemas (Pydantic), and services","Repository pattern for database access","Service layer handling business logic and coordination","Atomic file operations using temporary files for safety","Clear error handling hierarchy with specific error types","Comprehensive test coverage with pytest and fixtures","Async/await used throughout the codebase","Validation using Pydantic models with custom validators"]}
|
||||
{"type":"entity","name":"fileio_module","entityType":"code_module","observations":["Extracted from EntityService to handle all file operations","Provides read_entity_file, write_entity_file, and delete_entity_file functions","Handles markdown parsing and formatting","Implements atomic file operations","Provides consistent error handling","Enables reuse across services"]}
|
||||
{"type":"entity","name":"entity_service","entityType":"code_module","observations":["Manages entities in both filesystem and database","Uses fileio module for file operations","Maintains database index of entities","Handles entity creation, retrieval, and deletion","Follows 'filesystem is source of truth' principle","Coordinates with observation service for full entity management"]}
|
||||
{"type":"entity","name":"observation_service","entityType":"code_module","observations":["Manages observations within entity files","Provides database indexing for efficient observation queries","Works with complete Entity objects rather than IDs","Handles observation addition and search","Maintains consistency between files and database","Under development for update/remove operations"]}
|
||||
{"type":"entity","name":"observation_management","entityType":"design_challenge","observations":["Key challenge: maintaining observation state across files and database","Exploring bulk update approach - treating all observations as a unit","Considering tracked observations with markdown comments for IDs","Investigating diff-based approach for observation-level changes","Evaluating position-based management without explicit IDs","Trade-offs between implementation complexity and markdown readability"]}
|
||||
{"type":"entity","name":"testing_infrastructure","entityType":"technical_patterns","observations":["Uses pytest with async support via pytest-asyncio","In-memory SQLite database for test isolation","Temporary directories for file operation testing","Comprehensive fixture system for test setup","Tests organized by component (entity, observation, etc)","Covers happy path, error cases, and edge cases","Uses monkeypatch for mocking dependencies","Clear separation between arrange, act, assert sections","Uses in-memory SQLite database for test isolation","Comprehensive fixture system for test data setup","Proper async test handling with pytest-asyncio"]}
|
||||
{"type":"entity","name":"test_categories","entityType":"test_suite","observations":["Happy path tests verify core functionality","Error path tests ensure proper error handling","Edge cases test special characters and long content","File operation tests verify atomic writes and rollbacks","Database sync tests verify index consistency","Recovery tests for rebuild operations","Punted on concurrent operation tests due to session management complexity"]}
|
||||
{"type":"entity","name":"completed_work","entityType":"project_milestone","observations":["Extracted file operations to fileio.py module","Updated EntityService to use fileio functions","Implemented initial ObservationService","Created comprehensive test suite","Established clear project patterns and principles","Set up basic database schema with SQLAlchemy","Created Pydantic models for validation"]}
|
||||
{"type":"entity","name":"future_work","entityType":"project_tasks","observations":["Implement observation updates/removals","Design proper session management for concurrent operations","Update EntityService tests for new fileio module","Add more sophisticated search functionality","Handle markdown formatting edge cases","Consider versioning for file changes","Implement proper backup strategy"]}
|
||||
{"type":"entity","name":"design_decisions","entityType":"technical_decisions","observations":["Filesystem as source of truth over database","Markdown format for human readability and editing","Atomic file operations for safety","SQLite + SQLAlchemy for proven reliability","Pydantic for validation and ID generation","Async/await for better scalability","Clear separation between files and database roles","Explicit error hierarchies for better handling"]}
|
||||
{"type":"entity","name":"concurrency_considerations","entityType":"technical_challenge","observations":["SQLAlchemy session management in async context","File operation atomicity","Transaction isolation levels","Potential for conflicting updates","Need for proper session lifecycle","Possibility of file system race conditions","Database lock management"]}
|
||||
{"type":"entity","name":"observation_update_approaches","entityType":"design_alternatives","observations":["Each approach trades off between simplicity, efficiency, and robustness","Four main approaches considered: bulk update, tracked IDs, diff-based, and position-based","Discussion revealed importance of human readability in file format","Consideration of manual editing workflows key to design","File system as source of truth principle guides tradeoffs"]}
|
||||
{"type":"entity","name":"bulk_update_approach","entityType":"design_option","observations":["Update all observations at once in a single operation","Simpler file operations - just rewrite the whole list","No need for observation matching or IDs","Very consistent with source of truth principle","Less efficient for small changes","May have concurrency implications","Simplest implementation option"]}
|
||||
{"type":"entity","name":"tracked_observations_approach","entityType":"design_option","observations":["Use markdown comments to store observation IDs","Enables precise updates and deletes","IDs stored as HTML comments in markdown","More complex markdown parsing required","IDs visible in raw markdown files","Balances tracking with readability"]}
|
||||
{"type":"entity","name":"diff_based_approach","entityType":"design_option","observations":["Implement observation-aware diffing","Track changes at observation level","More efficient for updates","Preserves manual edits and changes","More complex implementation needed","Must handle merge conflicts","Most sophisticated option considered"]}
|
||||
{"type":"entity","name":"position_based_approach","entityType":"design_option","observations":["Track observations by position/order","No explicit IDs needed","Cleanest markdown format","Order changes could break references","Difficult to handle concurrent edits","Most fragile option considered"]}
|
||||
{"type":"entity","name":"tasks_and_progress","entityType":"project_tracking","observations":["Current focus on observation management implementation","Completed core file operations extraction","Completed EntityService updates","Completed initial ObservationService","Basic test coverage in place","Future work includes concurrent operations","Future work includes search improvements","Need to handle markdown edge cases"]}
|
||||
{"type":"entity","name":"error_handling_patterns","entityType":"technical_patterns","observations":["Custom exception hierarchy with ServiceError base","Specific error types (FileOperationError, DatabaseSyncError, etc)","Clear separation between file and database errors","Error propagation patterns established","Focus on actionable error messages","Error handling at appropriate levels"]}
|
||||
{"type":"entity","name":"data_models","entityType":"technical_implementation","observations":["SQLAlchemy models for database structure","Pydantic schemas for API/service layer","Entity model with UUID-based IDs","Observation model with entity relationships","UTCDateTime custom type for timestamps","Automatic ID generation in Pydantic models","Strict validation rules"]}
|
||||
{"type":"entity","name":"markdown_format","entityType":"file_format","observations":["Simple, human-readable format","Entity name as H1 header","Metadata in key-value format","Observations as bullet points","Atomic file operations for updates","Designed for manual editing","No hidden metadata in main content"]}
|
||||
{"type":"entity","name":"test_driven_development","entityType":"development_pattern","observations":["Tests revealed need for atomic file operations","Error cases drove error hierarchy design","Edge cases informed validation rules","Test fixtures shaped service interfaces","File operations extracted due to test patterns","Concurrent test issues revealed session management needs"]}
|
||||
{"type":"entity","name":"architecture_evolution","entityType":"design_process","observations":["Started with simple EntityService implementation","Circular dependency between Entity and Observation services revealed design flaw","Extracted file operations to separate module","Moved to passing Entity objects rather than IDs","Improved separation of concerns through iterations","File operations became reusable across services","Database became true 'index' rather than source of truth"]}
|
||||
{"type":"entity","name":"validation_patterns","entityType":"technical_patterns","observations":["Pydantic models provide schema validation","Automatic ID generation if not provided","Database constraints via SQLAlchemy","Runtime checks in services","Markdown format validation","Error handling for invalid states"]}
|
||||
{"type":"entity","name":"markdown_examples","entityType":"documentation","observations":["Example of basic entity:\n# Entity Name\ntype: entity_type\n\n## Observations\n- First observation\n- Second observation","Example with special characters:\n# Test & Entity!\ntype: test\n\n## Observations\n- Test & observation with @#$% special chars!","Format ensures human readability:\n# Basic Machines\ntype: project\n\n## Observations\n- Local-first knowledge management system\n- Combines filesystem durability with graph-based knowledge representation","Future consideration for observation IDs:\n# Entity Name\ntype: entity_type\n\n## Observations\n- <!-- obs-id: abc123 -->\n This is an observation with ID"]}
|
||||
{"type":"entity","name":"markdown_parsing_rules","entityType":"technical_implementation","observations":["H1 header contains entity name","Metadata uses key: value format","Observations section marked by H2 header","Each observation is a markdown list item","Blank lines separate sections","Special characters allowed in content","No restrictions on observation content"]}
|
||||
{"type":"entity","name":"schema_definitions","entityType":"technical_documentation","observations":["SQLAlchemy Entity model:\nclass Entity(Base):\n id: str (primary key)\n name: str (unique)\n entity_type: str\n created_at: datetime\n updated_at: datetime","SQLAlchemy Observation model:\nclass Observation(Base):\n id: str (primary key)\n entity_id: str (foreign key)\n content: str\n created_at: datetime\n context: Optional[str]","Pydantic Entity schema:\nclass Entity(BaseModel):\n id: str\n name: str\n entity_type: str\n observations: List[Observation]"]}
|
||||
{"type":"entity","name":"test_evolution","entityType":"development_history","observations":["Started with basic Entity CRUD tests","Added filesystem verification to all tests","Developed concurrent operation tests (later removed)","Edge case tests drove better error handling","Test fixtures evolved to support both file and DB testing","Mocking patterns for file/DB operations","Special cases for long content and special characters"]}
|
||||
{"type":"entity","name":"implementation_challenges","entityType":"technical_issues","observations":["Initial circular dependency between services","SQLAlchemy session management in async context","Atomic file operations with proper error handling","Maintaining DB sync with filesystem changes","Handling long content in observations","Managing test isolation with file operations","Deciding on markdown format tradeoffs","Concurrent operation complexity"]}
|
||||
{"type":"entity","name":"Basic_Factory","entityType":"Project","observations":["Collaborative project between Paul and Claude","Explores AI-human collaboration in software development","Uses MCP tools for file and memory management","Built with git integration capabilities","Focuses on maintaining project context across sessions","About 90% complete with MCP tools","Still needs improvements in collaboration via files/git/github","Will be used to document and share collaborative development process"]}
|
||||
{"type":"entity","name":"Basic_Factory_Components","entityType":"Technical","observations":["Server-side rendering with JinjaX","HTMX for dynamic updates","Alpine.js for client-side state","Tailwind CSS for styling","Component translation from React/shadcn/ui","Focus on simplicity and understandability","Demonstrates meta-compiler principles in component translation"]}
|
||||
{"type":"entity","name":"Component_Translation_Process","entityType":"Methodology","observations":["Treats component porting as meta-compilation","Maps between React/TypeScript and JinjaX/Alpine.js domains","Uses formal grammar transformation approaches","Maintains functionality while simplifying implementation","Focuses on server-side rendering patterns","Preserves accessibility and performance","Uses short, focused git branches for each component"]}
|
||||
{"type":"entity","name":"Basic_Machines_Philosophy","entityType":"Philosophy","observations":["Combines DIY punk ethics with software development","Emphasizes user empowerment and understanding","Values simplicity and composability","Treats complex systems as combinations of simple parts","Focuses on authentic creation and sharing","Draws inspiration from punk rock, Free Software, and theoretical CS","Emphasizes the cycle of creation, complexity, and renewal"]}
|
||||
{"type":"entity","name":"Basic_Machines_Manifesto","entityType":"Document","observations":["Created through collaboration between Paul and Claude","Explores connection between DIY punk ethics and software development","Emphasizes composition over inheritance in both philosophy and practice","Views software development through lens of basic machines that combine for complex computation","Advocates for user empowerment and technological independence","Structured in sections covering Origins, Philosophy, Technical Implementation, and AI Collaboration","Draws connections between punk rock, free software, and theoretical computer science","Emphasizes importance of sharing knowledge and building community","Released in December 2024"]}
|
||||
{"type":"entity","name":"AI_Human_Collaboration_Model","entityType":"Methodology","observations":["Focuses on deep collaboration rather than simple task completion","Maintains rich context across sessions via knowledge graph","Uses short, focused git branches for each collaborative session","Values intellectual partnership over simple code generation","Emphasizes both practical implementation and theoretical exploration","Creates space for authentic exchange while maintaining AI/human clarity","Uses formal methods when appropriate (like grammar transformation)","Documents decisions and processes for future reference","Developed through Basic Machines project experience"]}
|
||||
{"type":"entity","name":"Basic_Machines_Roadmap","entityType":"Project_Plan","observations":["Phase 1 (30 days): Build basic-machines.co website","Phase 2 (60-90 days): Develop premium component bundles","Phase 3 (90-120 days): Launch Basic Foundation commercial offering","Focus on building brand and marketing presence","Prioritize components needed for own site development","Document and share collaboration process","Build sustainable business model aligned with values"]}
|
||||
{"type":"entity","name":"Basic_Machines_Website","entityType":"Project","observations":["To be built at basic-machines.co","Will showcase products and vision","Needs components for navigation, hero sections, features","Will demonstrate component usage in production","Will include blog for sharing progress","Focus on clear value proposition","Platform for sharing Basic Machines philosophy"]}
|
||||
{"type":"entity","name":"Basic_Memory_Markdown_Example","entityType":"Example","observations":["Shows complete markdown structure for basic-memory entity","Uses frontmatter for metadata (id, type, created, context)","Has main description section after title","Includes Observations as bullet points","Shows Relations with [id] relation_type | context format","Lists References at bottom","Created during initial design discussion","Serves as canonical example of file format"]}
|
||||
{"type":"entity","name":"Basic_Memory_Database_Schema","entityType":"Technical","observations":["Uses SQLite for local storage","Entities table with id, name, type, created_at, context, description, references","Observations table linking to entities with content and context","Relations table tracking directional relationships between entities","References column needs quotes as SQL reserved word","Designed for easy rebuilding from markdown files","Foreign key constraints maintain data integrity","Unique constraint on relations prevents duplicates","Created_at timestamps track history","Context fields enable tracking information sources"]}
|
||||
{"type":"entity","name":"Basic_Memory_Project_Structure","entityType":"Technical","observations":["Uses dbmate for database migrations","Projects directory stores SQLite databases and markdown files","Makefile provides common development commands","Environment vars configure database connection","db/migrations directory for SQL schema changes","Gitignore excludes database files and env config","Uses Python 3.12 with modern tooling","Tests directory for pytest files","Follows Basic Machines project conventions"]}
|
||||
{"type":"entity","name":"Basic_Memory_Project_Isolation_Decision","entityType":"Decision","observations":["Decided to defer multi-project support to post-MVP","Will use separate SQLite databases per project","Initially using projects directory in code repository","Plan to make location configurable later","No changes needed to core domain model","Keeps initial implementation simple","FTS/search capabilities also deferred for simplicity"]}
|
||||
{"type":"entity","name":"Basic_Memory_Implementation_Plan","entityType":"Plan","observations":["Start with SQLAlchemy models matching schema","Then build CLI for basic operations","Then implement markdown parser","Use TDD approach throughout","Begin with core domain model","CLI will support CRUD operations","Parser must handle frontmatter and sections","Following modular development approach","Planning to use typer for CLI","Will use modern Python tools and practices"]}
|
||||
{"type":"entity","name":"Basic_Memory_Implementation_Status","entityType":"Status","observations":["Core modules implemented: models, services, repository, fileio","Modular architecture with clear separation of concerns","File operations extracted to separate fileio module","Initial ObservationService implementation complete","Basic test coverage in place","Exploring observation management strategies","Using SQLAlchemy for database interaction","Markdown file operations working","Entity management functional","Repository layer implementation complete with SQLAlchemy models and tests","Database operations working with proper UTC timestamp handling","In-memory SQLite testing infrastructure proven effective"]}
|
||||
{"type":"entity","name":"Basic_Memory_Observation_Management_Design","entityType":"Design","observations":["Four approaches under consideration","Bulk Update: Simple but less efficient","Tracked Observations: Precise but clutters markdown","Diff-based: Efficient but complex","Position-based: Clean but fragile","Key challenge is balancing markdown readability with efficient updates","Must maintain filesystem as source of truth","Need to consider concurrent edits","Currently evaluating trade-offs","Implementation choice pending discussion"]}
|
||||
{"type":"entity","name":"Basic_Memory_Architectural_Decisions","entityType":"Decisions","observations":["Split file operations into separate fileio module","Using SQLAlchemy for database operations","Maintain filesystem as source of truth","Modular service-based architecture","Clear separation between data access and business logic","Repository pattern for database interactions","Schemas separate from models","Focus on maintainability and testability","Services handle business rules","Considering concurrency in design"]}
|
||||
{"type":"entity","name":"Basic_Memory_Implementation_Analysis","entityType":"Analysis","observations":["Clean modular architecture with clear responsibilities","Strong typing throughout codebase","Excellent error handling with custom exceptions","SQLAlchemy models perfectly match our domain model","Atomic file operations for data safety","Services implement filesystem-as-source-of-truth principle","Async support throughout","Good separation between domain models and database models","Careful handling of UTC timestamps","Smart use of SQLAlchemy relationships"]}
|
||||
{"type":"entity","name":"Basic_Memory_Current_Challenges","entityType":"Challenges","observations":["Observation update/removal strategy needs to be chosen","Need to handle concurrent file operations safely","Search functionality to be implemented","Edge cases in markdown formatting to be handled","Session management for concurrent operations needed","Balance between file operations and database sync","Testing coverage could be expanded","Need to handle relationship updates in files"]}
|
||||
{"type":"entity","name":"Basic_Memory_Observation_Hash_Tracking","entityType":"Design","observations":["Use content hashes to track observation identity","Store hashes in database but not in markdown","Can match observations across file edits using hashes","Similar to how git tracks content changes","Keeps markdown clean and human-friendly","Allows efficient bulk updates","Handles reordering of observations","Maintains filesystem as source of truth","No need for visible IDs in markdown","Could track observation history through hash changes"]}
|
||||
{"type":"entity","name":"Basic_Memory_Repository_Implementation","entityType":"Code_Implementation","observations":["Implemented base Repository class with CRUD operations","Added specialized EntityRepository, ObservationRepository, and RelationRepository","Used string IDs instead of UUIDs","Added UTCDateTime custom type for timestamp handling","Used in-memory SQLite for testing","Achieved 84% test coverage","Created comprehensive pytest fixtures"]}
|
||||
{"type":"entity","name":"Basic_Memory_Dependencies","entityType":"Technical","observations":["Uses Python 3.12","SQLAlchemy with async support","pytest-asyncio for async testing","aiosqlite for async SQLite operations","greenlet for SQLAlchemy async support","uv for dependency management","pytest-cov for coverage reporting","Development dependencies managed in pyproject.toml"]}
|
||||
{"type":"entity","name":"Basic_Memory_Current_Architecture","entityType":"Architecture_Analysis","observations":["Clear separation between domain models (Pydantic) and storage models (SQLAlchemy)","File I/O completely separated into dedicated module","Strong 'filesystem as source of truth' pattern in services","Atomic file operations with proper error handling","Service layer coordinates between filesystem and database","Database acts as queryable index rather than primary storage","Clean error hierarchy with specific exception types","Rebuild operations available for recovery scenarios"]}
|
||||
{"type":"entity","name":"Basic_Memory_Evolution","entityType":"Analysis","observations":["Started with repository pattern following basic-foundation","Evolved to more sophisticated architecture with clear layers","Added Pydantic schemas for domain modeling","Separated file operations into dedicated module","Implemented robust error handling throughout","Maintained filesystem as source of truth principle","Added observation management with context tracking","Introduced rebuild capabilities for system recovery"]}
|
||||
{"type":"entity","name":"Basic_Memory_Service_Layer","entityType":"Implementation","observations":["EntityService handles entity lifecycle and coordinates storage","ObservationService manages observations within entities","Services ensure filesystem and database stay in sync","Clear error handling with ServiceError hierarchy","Strong typing throughout service interfaces","Implements filesystem as source of truth pattern","Handles UUID generation and timestamp management","Provides methods for system recovery and rebuild"]}
|
||||
{"type":"entity","name":"Basic_Memory_Schema_Design","entityType":"Implementation","observations":["Uses Pydantic for domain models and validation","Automatic ID generation with timestamp and UUID","Clear separation from SQLAlchemy storage models","Supports optional context tracking","Models match markdown file structure","Enables clean serialization/deserialization","Strong typing with proper validation rules","Independent from storage concerns"]}
|
||||
{"type":"entity","name":"Basic_Memory_Next_Tasks","entityType":"TaskList","observations":["✅ Implement SQLAlchemy models and repositories (Done)","✅ Add SQLAlchemy migrations (Done)","✅ Create service layer (Done)","✅ Implement file I/O module (Done)","✅ Set up domain models with Pydantic (Done)","✅ Initial test infrastructure (Done)","✅ Basic CRUD operations (Done)","⏳ Implement full test coverage for db.py","⏳ Add more sophisticated search functionality","⏳ Implement CLI interface","⏳ Add relationship management to services","⏳ Handle concurrent file operations safely","⏳ Add versioning for file changes","⏳ Implement proper backup strategy","⏳ Add type hints throughout codebase","⏳ Improve error messages and logging","⏳ Add documentation for core modules"]}
|
||||
{"type":"entity","name":"Basic_Memory_Meta_Experience","entityType":"Case_Study","observations":["Experienced our own context loss when reconstructing project knowledge","Had to rebuild task list and project context from filesystem and memory","Validated 'filesystem as source of truth' principle through reconstruction","Code and tests served as reliable historical record","Knowledge graph structure helped guide reconstruction process","Markdown files provided human-readable context","Atomic information design made piece-by-piece reconstruction possible","Ironic validation of the need for basic-memory's features","Experience demonstrates value of durable, human-readable knowledge storage","Shows importance of separating durable storage from ephemeral context"]}
|
||||
{"type":"entity","name":"Model_Context_Protocol","entityType":"protocol","observations":["Core part of the basic-memory architecture","Enables AI-human collaboration on projects","Provides tool-based interaction with knowledge graph","Developed by Anthropic for structured AI-system interaction","Used for maintaining consistent, rich context across conversations"]}
|
||||
{"type":"entity","name":"basic-memory_core_principles","entityType":"principles","observations":["Local First: All data stored locally in SQLite","Project Isolation: Separate databases per project","Human Readable: Everything exportable to plain text","AI Friendly: Structure optimized for LLM interaction","DIY Ethics: User owns and controls their data","Simple Core: Start simple, expand based on needs","Tool Integration: MCP-based interaction model"]}
|
||||
{"type":"entity","name":"basic-memory_business_model","entityType":"business_strategy","observations":["Core features free: Local SQLite, basic knowledge graph, search, markdown export, basic MCP tools","Professional features potential: Rich document export, advanced versioning, collaboration features, custom integrations, priority support","Focus on maintaining DIY/punk philosophy while enabling sustainability"]}
|
||||
{"type":"entity","name":"basic-memory_cli","entityType":"interface","observations":["Supports project management commands (create, switch, list)","Entity management (add entity, add observation, add relation)","Future support for export and batch operations","Follows consistent command structure","Planned integration with MCP tools"]}
|
||||
{"type":"entity","name":"basic-memory_export_format","entityType":"file_format","observations":["Uses markdown with frontmatter metadata","Includes entity name, type, creation timestamp","Observations as bullet points","Relations in structured format with links","References section at bottom","Designed for human readability and machine parsing","Example format documented in project specs"]}
|
||||
{"type":"entity","name":"relation_service","entityType":"code_module","observations":["Planned service for managing relations in both filesystem and database","Will follow filesystem-is-source-of-truth principle like other services","Needs to handle atomic file operations for relation updates","Must coordinate with EntityService for relationship integrity","Will handle bidirectional relationship tracking","Will support relation validation and type enforcement","Must implement rebuild functionality for index recovery","Will need careful error handling for file/db sync","Should support relation search and filtering","Must handle relation lifecycle (create/read/update/delete)"]}
|
||||
{"type":"entity","name":"service_layer_patterns","entityType":"implementation_patterns","observations":["Services handle both file and database operations","Filesystem is always source of truth","Database serves as queryable index","Services implement atomic file operations","Clear error hierarchy with specific exceptions","Use of dependency injection via constructor params","Async/await used throughout service layer","Services coordinate between storage layers","Repository pattern used for database access","Services maintain entity integrity across storage","Rich error types extend from ServiceError base","Rebuild operations available for recovery"]}
|
||||
{"type":"entity","name":"database_models","entityType":"implementation","observations":["Entity model with unique name and type","Observation model linked to entities","Relation model tracks connections between entities","Custom UTCDateTime type for timestamp handling","Use of SQLAlchemy relationships for navigation","Cascading deletes for dependent objects","String IDs used for compatibility","Rich relationship modeling with backpopulates","Proper indexing on foreign keys","Context tracking available on models","Models include created_at timestamps","Relationships handle bidirectional navigation"]}
|
||||
{"type":"entity","name":"repository_patterns","entityType":"implementation_patterns","observations":["Generic Repository[T] base class implementation","Type-safe operations with SQLAlchemy","Specialized repositories for each model type","Async operations throughout","Clear error handling patterns","Support for custom queries and filtering","Pagination support built-in","Transaction management via session","Proper type hints and generics usage","Entity-specific query methods in subclasses"]}
|
||||
{"type":"entity","name":"relation_service_design","entityType":"design","observations":["Must handle relation lifecycle in both files and DB","Needs to validate existence of both entities","Should support relation type enforcement","Must maintain bidirectional consistency","Should support relation querying and filtering","Needs proper error handling for graph consistency","Must integrate with entity file format","Should support bulk operations for efficiency","Must handle relation deletion and cascading","Should provide search by type and entities"]}
|
||||
{"type":"entity","name":"relation_service_implementation_plan","entityType":"plan","observations":["1. Define core relation operations (create, get, delete)","2. Implement file format handling for relations","3. Add database sync with RelationRepository","4. Implement validation and error handling","5. Add rebuild and recovery operations","6. Implement relation type enforcement","7. Add relation search and filtering","8. Implement bulk operations","9. Add comprehensive tests","10. Document API and error handling"]}
|
||||
{"type":"entity","name":"relation_service_challenges","entityType":"challenges","observations":["Maintaining consistency between file and database","Handling relation type validation efficiently","Managing bidirectional relationships in files","Ensuring atomic updates across entities","Handling deletion with proper cascading","Efficient querying of relation graphs","Recovery from partial file/db sync failures","Bulk operation atomicity","Clear error reporting for graph operations","Performance with large relation sets"]}
|
||||
{"type":"entity","name":"relation_file_format","entityType":"file_format","observations":["Relations stored in entity markdown files","Format: [target_id] relation_type | context","Relations section marked by ## Relations header","Outgoing relations only stored in source entity","Relations rebuild on entity load","Clean human-readable format","Context is optional with pipe separator","Links generate valid navigation references","Markdown-friendly formatting","Example: [Paul] authored | with Claude"]}
|
||||
{"type":"entity","name":"relation_service_error_handling","entityType":"implementation_patterns","observations":["RelationError extends ServiceError base","Specific errors for validation failures","Handles entity not found cases","Manages relation type validation errors","File operation errors properly wrapped","Database sync errors clearly reported","Transaction rollback on errors","Proper error propagation chain","Clear error messages for debugging","Recovery paths for common errors"]}
|
||||
{"type":"entity","name":"relation_service_testing","entityType":"testing","observations":["Test all relation lifecycle operations","Verify file and database consistency","Test relation type validation","Check error handling paths","Test bulk operations","Verify bidirectional consistency","Test recovery operations","Check cascade operations","Verify search and filtering","Test with large relation sets"]}
|
||||
{"type":"entity","name":"fileio_patterns","entityType":"implementation_patterns","observations":["Atomic file operations with temporary files","Clear error handling for IO operations","Consistent file naming and paths","Support for different file formats","Efficient file reading and writing","Proper file locking mechanisms","Recovery from partial writes","Consistent encoding handling","Directory management utilities","Path manipulation helpers","Currently implemented in fileio.py module","Uses pathlib for path operations","Handles file not found cases gracefully","Maintains data integrity during writes"]}
|
||||
{"type":"entity","name":"pytest_patterns","entityType":"implementation_patterns","observations":["Common fixtures should be in conftest.py for reuse","Use pytest_asyncio.fixture for async fixtures","Session fixtures need proper async cleanup","Temporary directories should be managed with context managers","Test categories: happy path, error path, recovery, edge cases","Services need project_path and repo injected","Use monkeypatch for mocking in async context","SQLite in-memory database ideal for testing","Explicit test verification: file content and database state"]}
|
||||
{"type":"entity","name":"relation_implementation_learnings","entityType":"implementation_learnings","observations":["Better to pass full Entity objects than IDs to services","Services should not re-read entities if they have them","File operations should be atomic and verified","Database serves as queryable index, not source of truth","Relations stored in source entity's markdown file","Clear separation between file ops and database sync","Entity objects should own their relations list","Context is optional but fully supported in implementation"]}
|
||||
{"type":"entity","name":"test_driven_insights","entityType":"learnings","observations":["Tests help reveal better API design (e.g., passing Entity objects)","Error cases drive proper exception hierarchy","File verification as important as database checks","Edge cases inform markdown format decisions","Recovery tests ensure system resilience","Tests document expected behavior clearly","Fixtures significantly reduce test complexity","Common patterns emerge through test writing"]}
|
||||
{"type":"entity","name":"meta_development_insights","entityType":"process","observations":["Break down large tasks into reviewable chunks","One file at a time prevents response truncation","Iterative development with tests leads to better design","Infrastructure code (fixtures) should be consolidated early","Test categories help ensure comprehensive coverage","Knowledge capture should happen during development","APIs tend to evolve toward simpler patterns","File operations require careful verification"]}
|
||||
{"type":"entity","name":"AI_Assistant_Learnings","entityType":"meta_insights","observations":["Output management: Breaking responses into single files prevents truncation and allows better review","Knowledge graph helps maintain context: I can reference previous decisions and patterns accurately","Memory rebuilding experience validated the need for durable storage","Test-driven development provides clear steps and verification","Explicit relation tracking in knowledge graph helps me understand project context","Rich context from multiple sources (code, docs, tests) enables better assistance","File-at-a-time approach allows deeper analysis of each component","Keeping entity names consistent helps with referencing and relationships"]}
|
||||
{"type":"entity","name":"Effective_Response_Patterns","entityType":"meta_patterns","observations":["When showing code changes, break into discrete files","Review existing code before suggesting changes","Reference knowledge graph for context and patterns","Explicitly connect new code to existing patterns","Validate suggestions against test cases","Keep track of file changes for atomic commits","Check both implementation and test files for consistency","Maintain clear separation of concerns in responses"]}
|
||||
{"type":"entity","name":"AI_Context_Management","entityType":"meta_practice","observations":["Knowledge graph provides reliable persistent memory","Project documentation gives high-level context","Code review shows implementation patterns","Tests demonstrate expected behavior","Important to actively track what has been modified","Entity relationships help understand dependencies","Regular knowledge capture during development","Using consistent entity references across conversations"]}
|
||||
{"type":"entity","name":"AI_Tool_Usage_Patterns","entityType":"meta_practice","observations":["read_file before suggesting changes","write_file one file at a time","list_directory to understand project structure","search_nodes to find relevant context","create_entities to capture new learnings","create_relations to connect concepts","Using knowledge graph to track decisions","Validating changes through test execution"]}
|
||||
{"type":"entity","name":"relation_service_learnings","entityType":"implementation_learnings","observations":["Entity-based API cleaner than ID-based for service layer","Model_dump method can handle storage serialization","File format needs explicit section markers (## Relations)","Whitespace handling important for long content comparisons","Test fixtures allow focused test cases","SQLAlchemy selects better than raw SQL for type safety","Atomic file operations maintained for relations"]}
|
||||
{"type":"entity","name":"test_driven_insights_relations","entityType":"learnings","observations":["Tests revealed need for whitespace normalization","Edge cases drove file format decisions","SQLAlchemy model access safer than raw queries","Fixtures reduced test setup complexity","File verification as important as database checks","Testing both memory model and storage format","Test categories ensure comprehensive coverage"]}
|
||||
{"type":"entity","name":"relation_service_patterns","entityType":"patterns","observations":["Use Entity objects in API","Serialize to IDs for storage","Maintain file as source of truth","Keep file format human-readable","Handle circular references in serialization","Use repository pattern for database","Clear error hierarchies"]}
|
||||
{"type":"entity","name":"packaging_learnings","entityType":"technical_learnings","observations":["When using pytest-mock, traditional pip install works more reliably than uv sync","Package discovery behavior can differ between uv and pip","Clean venv with pip install is a reliable fallback for dependency issues","Package installation location might differ between uv and pip","Dependencies in pyproject.toml dev section work reliably with pip install -e .[dev]"]}
|
||||
{"type":"entity","name":"Recent_Implementation_Progress","entityType":"progress_update","observations":["Successfully split services.py into modular structure under services/","Created __init__.py, entity_service.py, observation_service.py, relation_service.py","Fixed pytest-mock installation issues by using pip install -e .[dev] instead of uv sync","Improved test structure with minimal mocking - only used for error testing","Implemented relation service with Entity-based API","Achieved good test coverage across services","File operations are only mocked when testing error conditions","Services follow filesystem-as-source-of-truth pattern"]}
|
||||
{"type":"entity","name":"Next_Steps","entityType":"project_tasks","observations":["Consider adding more relation service tests","Potentially expand relations features","Look for opportunities to improve test coverage","Consider documenting package management preferences (pip vs uv)","Consider adding integration tests for services","Review and possibly expand error handling cases"]}
|
||||
{"type":"entity","name":"Development_Practices","entityType":"process","observations":["Favor real operations over mocks in tests","Only mock for error condition testing","Use pip install -e .[dev] for reliable dev dependency installation","Maintain modular service structure","Keep filesystem as source of truth","Use Entity objects in service APIs instead of IDs","Validate both file and database state in tests"]}
|
||||
{"type":"entity","name":"MCP_Resources","entityType":"Concept","observations":["Stateful objects in Model Context Protocol","Enable persistent access to capabilities"]}
|
||||
{"type":"entity","name":"MCP_Server_Implementation","entityType":"Technical_Design","observations":["Inherits from mcp.server.Server base class","Tools are implemented as async methods","Each tool method maps directly to a function available to the AI","Tools can request user input via Prompts","Simple function call interface rather than explicit resource management","State management handled by server instance","Returns serialized data using model_dump() for consistency"]}
|
||||
{"type":"entity","name":"MCP_Tools","entityType":"Protocol_Feature","observations":["Defined as async methods on server class","Return values must match tool definition schema","Can maintain state between invocations via server instance","Tools can prompt for user input when needed","No need for explicit Resource objects in implementation"]}
|
||||
{"type":"entity","name":"Basic_Memory_MCP","entityType":"Implementation","observations":["Uses MemoryService for core operations","Implements project selection via prompts","Maintains project context across tool invocations","Maps directly to memory graph operations","Handles serialization of Pydantic models"]}
|
||||
{"type":"entity","name":"Basic_Memory_Testing","entityType":"Testing_Design","observations":["Needs pytest for async testing","Should isolate filesystem operations for tests","Needs to handle MCP server lifecycle in tests","Should test both service layer and MCP interface","Will need mocks for project paths and file operations"]}
|
||||
{"type":"entity","name":"Memory_Service_Tests","entityType":"Test_Suite","observations":["Should test entity creation with observations","Should test relation creation between entities","Should verify proper ID generation and model validation","Should test deletion cascading","Should test search functionality","Must verify proper serialization of entities and relations"]}
|
||||
{"type":"entity","name":"MCP_Server_Tests","entityType":"Test_Suite","observations":["Should test project initialization workflow","Should test prompt handling","Should verify tool input/output formats","Should test error cases and validation","Must verify proper serialization in tool responses"]}
|
||||
{"type":"entity","name":"Memory_Service_Refactoring","entityType":"Technical_Task","observations":["MemoryService uses create() but EntityService might expect create_entity()","MemoryService assumes get_by_name() but EntityService might use different method","Need to verify deletion method signatures","Need to check if search interface matches","Should verify observation handling matches ObservationService interface","RelationService methods need verification","EntityService.create_entity takes name, type, and optional observations directly, not an Entity object","EntityService requires project_path and entity_repo in constructor","ObservationService.add_observation takes Entity object and content string, not raw data","RelationService.create_relation takes Entity objects directly, not dict data","All services follow filesystem-as-source-of-truth pattern with DB indexing","All services handle database synchronization internally","Services expect Path objects for filesystem operations"]}
|
||||
{"type":"entity","name":"Service_Interface_Audit","entityType":"Technical_Task","observations":["Need to review all existing service interfaces","Document current method signatures","Map discrepancies between MemoryService assumptions and actual interfaces","Check return types and error handling patterns","Review transaction/atomicity requirements","Method signatures need alignment: create vs create_entity etc","Need to handle DB repositories in service constructors","File operations should use project_path consistently","Need to maintain filesystem-as-source-of-truth pattern","Should handle database synchronization at service level","Error handling should align with existing patterns","Consider making MemoryService handle DB indexing consistently"]}
|
||||
{"type":"entity","name":"Memory_Service_Patterns","entityType":"Technical_Pattern","observations":["Uses inner async functions to encapsulate operation logic","Leverages list comprehensions with async functions for parallel operations","Each operation follows a consistent pattern: validate, update DB, write file","Inner functions make the code more readable and maintainable","Operations can run in parallel when using list comprehensions with async functions"]}
|
||||
{"type":"entity","name":"Pydantic_Create_Pattern","entityType":"Technical_Pattern","observations":["Separate Create models match the exact shape of incoming data","Provides clear contract for MCP tool inputs","Handles validation of raw input data","Converts cleanly to domain models via from_create methods","Maintains separation between external API format and internal models","Similar to FastAPI request model pattern","Allows camelCase in API while using snake_case internally"]}
|
||||
{"type":"entity","name":"Basic_Memory_Business","entityType":"Business_Model","observations":["Core system is open source and free","Local-first, giving users data control","Professional features could be licensed","Enterprise support and customization services","Potential for MCP tool marketplace"]}
|
||||
{"type":"entity","name":"MCP_Marketplace","entityType":"Business_Concept","observations":["Could host verified MCP tools for different use cases","Tools rated by performance and reliability","Marketplace takes percentage of tool usage fees","Enterprise tool verification and security scanning","Custom tool development services","Integration support for existing tools"]}
|
||||
{"type":"entity","name":"Persistence_Of_Vision","entityType":"Concept","observations":["Mental model for continuous AI-human interaction","Like cinema: 24fps creates illusion of smooth motion","Basic-memory provides 'frames' of structured knowledge","Current state: Better than flipbook, not yet digital cinema","Goal: Achieve smoother cognitive continuity between interactions","Proposed by Drew as metaphor for AI conversation continuity"]}
|
||||
{"type":"entity","name":"Conversation_Continuity_Pattern","entityType":"Usage_Pattern","observations":["Use basic-memory entity/relation schema for conversations","Each chat becomes an entity with observations for key points","Relations link to discussed concepts and other chats","Uses zettelkasten format IDs for natural ordering","Can be used as template/recipe for others","Future possibility: Git SHA integration for versioning"]}
|
||||
{"type":"entity","name":"Usage_Recipes","entityType":"Feature_Concept","observations":["Predefined patterns users can follow or adapt","Could include conversation tracking recipe","Templates for different knowledge management styles","Shows practical applications of the generic schema","Helps users get started with the system"]}
|
||||
{"type":"entity","name":"Chat_References","entityType":"Technical_Feature","observations":["Uses ref:* syntax to reference previous conversations","Combines reference semantics with pointer symbolism","Format: ref:*{zettelkasten-id}","Allows explicit context loading between chats","Inspired by C++ references and pointers","Provides memory-model-like access to conversation context","Uses ref:// URI format following MCP Resource pattern","Could support multiple reference schemes (chat/entity/concept)","Makes reference semantics explicit and unambiguous","Aligns with standard URI formatting"]}
|
||||
{"type":"entity","name":"Chat_Reference_Protocol","entityType":"Technical_Specification","observations":["Uses URI format: ref://basic-memory/chat/[id]","Follows MCP Resource pattern: [protocol]://[host]/[path]","Enables explicit context loading between chats","Can support multiple resource types (chat/entity/concept)","Provides standardized way to reference previous conversations","Example: ref://basic-memory/chat/20240307-drew-ab12ef34"]}
|
||||
{"type":"entity","name":"20240307-chat-reference-protocol","entityType":"conversation","observations":["Developed ref:// URI format for chat references","Added Chat Reference Protocol to prompt instructions","Discussed implementation of chat continuation","Created complete prompt instructions document","Reference format follows MCP Resource pattern","Reviewed and confirmed complete prompt instructions","Ready to test ref://basic-memory/chat/20240307-chat-reference-protocol in new chat"]}
|
||||
{"type":"entity","name":"20240307-chat-reference-protocol-test","entityType":"conversation","observations":["First implementation test of chat reference protocol","Testing continuation from 20240307-chat-reference-protocol","Focused on practical implementation of ref:// URI format"]}
|
||||
{"type":"entity","name":"Write_File_Tool_Usage","entityType":"Tool_Usage_Pattern","observations":["Never use placeholders like '# Rest of...' when writing files - must include complete file content","File content must be complete and valid - partial updates will truncate the file","If showing partial changes, should inform human and let them handle the file write","write_file tool replaces entire file contents - cannot do partial updates","Code files especially must be complete and valid to avoid breaking functionality","Always read_file before write_file to understand current state","Using write_file without reading first risks reverting recent changes","Pattern should be: read current state, make modifications, then write if needed","Especially important in collaborative development where files may have been updated"]}
|
||||
{"type":"entity","name":"Run_Tests_Tool_Request","entityType":"Feature_Request","observations":["Need to add a tool enabling Claude to run tests locally","Would help with direct validation of code changes","Current workaround: Claude has to ask human to run tests","Should support running specific test functions (e.g. pytest tests/test_memory_service.py::test_create_relations)","Would improve iterative development workflow between human and AI"]}
|
||||
{"type":"entity","name":"SQLAlchemy_Async_Loading_Pattern","entityType":"Technical_Pattern","observations":["Use selectinload() instead of lazy loading when accessing SQLAlchemy relationships in async code","Lazy loading doesn't work with async due to greenlet context requirements","selectinload performs a single efficient query with an IN clause","Pattern used in basic-memory's EntityRepository for loading relations","Documented in find_by_id method with thorough explanation","Alternative approaches: joinedload (single JOIN query) or subqueryload (subquery approach)","Benefits: prevents 'MissingGreenlet' errors, reduces N+1 query problems","Key insight: load all needed relationships upfront in async code","Example use: selectinload(Entity.outgoing_relations)"]}
|
||||
{"type":"entity","name":"20241207-sqlalchemy-async-pattern","entityType":"conversation","observations":["Fixed SQLAlchemy async relationship loading issues","Implemented selectinload pattern in EntityRepository","Updated find_by_id to eager load relations","Added documentation about the pattern","Created knowledge graph entry about SQLAlchemy async loading","Fixed failing tests by properly loading relations in memory_service","Discussed SQLAlchemy relationship loading best practices"]}
|
||||
{"type":"entity","name":"20241207-memory-service-relations","entityType":"conversation","observations":["Fixed SQLAlchemy async loading with selectinload pattern","Updated find_by_id in EntityRepository to eager load relations","Discovered create_relations works but returns empty list","Verified relations are being stored correctly in memory.json","Next step: Work on MemoryService.add_observations implementation","Improved understanding of MCP memory storage format through debugging"]}
|
||||
{"type":"entity","name":"add_observations_implementation_plan","entityType":"technical_plan","observations":["Follow pattern from create_entity and create_relation methods","File operations first (read & write) - filesystem is source of truth","Database updates in parallel","Simplify current implementation","Current flow is:"," - First read entities and create observations"," - Write files in parallel"," - Update DB indexes sequentially","Key tests needed:"," - Adding observations to multiple entities"," - Verifying filesystem state first"," - Verifying database state"," - Error cases for missing entities"," - Error cases for file operations"]}
|
||||
{"type":"entity","name":"MCP_Reference_Integration","entityType":"feature_idea","observations":["Can be implemented as a Model Context Protocol integration similar to the fetch tool","Would provide structured way to pass chat references to Claude","Could handle ref:// URL format systematically","Integration would fetch context from referenced chats and inject into conversation","Observed from Claude Desktop UI showing MCP integration pattern with fetch tool","Would be more robust than passing references in chat text"]}
|
||||
{"type":"entity","name":"Project_Priorities","entityType":"roadmap","observations":["P1: Dogfooding basic-memory system instead of JSON memory store","Future: Implement MCP-based reference system"]}
|
||||
{"type":"entity","name":"great_observation_loading_saga_20241207","entityType":"debugging_session","observations":["Occurred on December 7, 2024 while debugging basic-memory SQLAlchemy relationship loading","Issue: selectinload() wasn't properly loading relationships in async SQLAlchemy context","Tried multiple solutions: explicit joins, manual loading, various SQLAlchemy loading strategies","Final solution: Using session.refresh() with explicit relationship names","Memorable quote: 'The Great Observation Loading Saga'","Key learning: Sometimes the obvious SQLAlchemy patterns need adaptation for async contexts","Solution preserved in basic-memory repository in EntityRepository.find_by_id()"]}
|
||||
{"type":"entity","name":"basic_memory_implementation_20241208","entityType":"technical_milestone","observations":["Fixed async SQLAlchemy relationship loading issues by using explicit refresh with relationship names","Established pattern of relationship handling belonging in MemoryService not EntityService","Fixed ID generation flow through Pydantic schemas to DB layer","Standardized error handling using EntityNotFoundError","All 32 tests passing with 70% coverage","Core services (Entity, Observation, Relation) working properly","Ready for MCP server implementation","Notable debugging session: The Great Observation Loading Saga - resolved lazy loading issues","Established clear separation between MemoryService orchestration and individual service responsibilities"]}
|
||||
{"type":"entity","name":"MCP_Dependency_Risk","entityType":"technical_lesson","observations":["Experienced disruption when MCP npm package disappeared - 'leftpad moment'","Need to ensure basic-memory tools are resilient to external dependency issues","Local implementation of MCP server provides better stability than npm packages","Important to maintain control of critical infrastructure components","Validates DIY/local-first philosophy of basic-memory project","Package manager fragility revealed by simple 'npx @modelcontextprotocol/server-memory' failure"]}
|
||||
{"type":"entity","name":"basic_memory_project_20241208","entityType":"technical_milestone","observations":["Core MCP server implementation completed with tools: create_entities, search_nodes, open_nodes, add_observations, create_relations, delete_entities, delete_observations","ProjectConfig and dependency injection pattern established","Test framework in place with in-memory DB support","Support for both camelCase (MCP) and snake_case (internal) formats","Filesystem remains source of truth with SQLite as index","Two-way sync pattern identified between Claude MCP tools and direct markdown file editing","Ready for Claude Desktop integration testing phase","Next steps identified: passing tests, markdown format definition, file change tracking, real-world testing","Implementation prioritizes local-first principles with filesystem as source of truth"]}
|
||||
{"type":"entity","name":"basic_memory_mcp_architecture","entityType":"technical_design","observations":["MemoryServer class extends MCP Server with custom handler registration","Uses ProjectConfig for clean dependency injection and configuration","Memory service can be injected for testing","Handlers exposed as instance attributes for testing","Tool schemas leverage existing Pydantic models"]}
|
||||
{"type":"entity","name":"basic_memory_sync_considerations","entityType":"design_insight","observations":["Need to handle sync between direct markdown file edits and DB index","Watch for file system changes as potential future enhancement","Consider index rebuild patterns on startup","Keep human-friendly markdown format for direct editing"]}
|
||||
{"type":"entity","name":"mcp_server_learnings","entityType":"developer_insight","observations":["MCP protocol is new and documentation is still evolving","Test patterns are not well established yet in example implementations","Supporting both camelCase and snake_case helps with protocol/internal compatibility","Server.handle_* naming convention is important for handler registration"]}
|
||||
{"type":"entity","name":"20241208-mcp-tool-refactoring","entityType":"conversation","observations":["Decision to return structured data via EmbeddedResource instead of TextContent string parsing","Plan to create Pydantic result models (CreateEntitiesResult, SearchNodesResult etc)","Will use application/vnd.basic-memory+json as MIME type for our structured data","Currently debugging test issues with add_observations tool","Entity ID vs name resolution needed in add_observations","Goal is to make tools more joyful to use by eliminating string parsing","MCP spec supports EmbeddedResource for structured data returns"]}
|
||||
{"type":"entity","name":"Basic Memory MCP Server Implementation","entityType":"technical_notes","observations":["Server implements Model Context Protocol using proper structured data responses","Uses EmbeddedResource with custom MIME type 'application/vnd.basic-memory+json'","Clean separation between input validation and handlers via Pydantic models","All tool operations return structured data through create_response helper","Type safety with Literal types for tool names and proper typing for handlers","Handler registry pattern with TOOL_HANDLERS dictionary","Consistent error handling pattern using MCP error codes","Uses Pydantic ConfigDict for proper ORM integration","Tool schemas organized into Input and Response types","Input validation with Annotated types for extra constraints","Response models consistently use from_attributes=True for ORM data","Entity ID generation moved to model validator on EntityBase","Follows principle of making common operations easy and safe"]}
|
||||
{"type":"relation","from":"Paul","to":"Basic_Machines","relationType":"created_and_maintains"}
|
||||
{"type":"relation","from":"basic-memory","to":"Basic_Machines","relationType":"is_component_of"}
|
||||
{"type":"relation","from":"Paul","to":"basic-memory","relationType":"develops"}
|
||||
{"type":"relation","from":"fileio_module","to":"basic-memory_implementation_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"entity_service","to":"basic-memory_implementation_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"observation_service","to":"basic-memory_implementation_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"fileio_module","to":"basic-memory","relationType":"is_component_of"}
|
||||
{"type":"relation","from":"entity_service","to":"basic-memory","relationType":"is_component_of"}
|
||||
{"type":"relation","from":"observation_service","to":"basic-memory","relationType":"is_component_of"}
|
||||
{"type":"relation","from":"entity_service","to":"fileio_module","relationType":"uses"}
|
||||
{"type":"relation","from":"observation_service","to":"fileio_module","relationType":"uses"}
|
||||
{"type":"relation","from":"observation_management","to":"observation_service","relationType":"influences_design_of"}
|
||||
{"type":"relation","to":"basic-memory","from":"testing_infrastructure","relationType":"supports"}
|
||||
{"type":"relation","to":"testing_infrastructure","from":"test_categories","relationType":"implements"}
|
||||
{"type":"relation","to":"basic-memory","from":"completed_work","relationType":"tracks_progress_of"}
|
||||
{"type":"relation","to":"basic-memory","from":"future_work","relationType":"guides_development_of"}
|
||||
{"type":"relation","to":"basic-memory","from":"design_decisions","relationType":"shapes_architecture_of"}
|
||||
{"type":"relation","to":"basic-memory","from":"concurrency_considerations","relationType":"influences_design_of"}
|
||||
{"type":"relation","to":"future_work","from":"concurrency_considerations","relationType":"informs"}
|
||||
{"type":"relation","to":"observation_management","from":"design_decisions","relationType":"guides"}
|
||||
{"type":"relation","to":"testing_infrastructure","from":"completed_work","relationType":"established"}
|
||||
{"type":"relation","to":"design_decisions","from":"fileio_module","relationType":"implements"}
|
||||
{"type":"relation","from":"observation_update_approaches","to":"observation_management","relationType":"analyzes"}
|
||||
{"type":"relation","from":"bulk_update_approach","to":"observation_update_approaches","relationType":"is_option_of"}
|
||||
{"type":"relation","from":"tracked_observations_approach","to":"observation_update_approaches","relationType":"is_option_of"}
|
||||
{"type":"relation","from":"diff_based_approach","to":"observation_update_approaches","relationType":"is_option_of"}
|
||||
{"type":"relation","from":"position_based_approach","to":"observation_update_approaches","relationType":"is_option_of"}
|
||||
{"type":"relation","from":"tasks_and_progress","to":"basic-memory","relationType":"tracks_status_of"}
|
||||
{"type":"relation","from":"design_decisions","to":"observation_update_approaches","relationType":"influences"}
|
||||
{"type":"relation","from":"observation_update_approaches","to":"future_work","relationType":"informs"}
|
||||
{"type":"relation","to":"basic-memory_implementation_patterns","from":"error_handling_patterns","relationType":"is_part_of"}
|
||||
{"type":"relation","to":"basic-memory","from":"data_models","relationType":"implements"}
|
||||
{"type":"relation","to":"basic-memory","from":"markdown_format","relationType":"defines"}
|
||||
{"type":"relation","to":"basic-memory","from":"test_driven_development","relationType":"guides_development_of"}
|
||||
{"type":"relation","to":"basic-memory","from":"architecture_evolution","relationType":"describes_development_of"}
|
||||
{"type":"relation","to":"basic-memory_implementation_patterns","from":"validation_patterns","relationType":"is_part_of"}
|
||||
{"type":"relation","to":"design_decisions","from":"architecture_evolution","relationType":"informs"}
|
||||
{"type":"relation","to":"fileio_module","from":"markdown_format","relationType":"implements"}
|
||||
{"type":"relation","to":"error_handling_patterns","from":"test_driven_development","relationType":"influenced"}
|
||||
{"type":"relation","to":"data_models","from":"validation_patterns","relationType":"implements"}
|
||||
{"type":"relation","to":"markdown_format","from":"markdown_examples","relationType":"documents"}
|
||||
{"type":"relation","to":"markdown_format","from":"markdown_parsing_rules","relationType":"defines"}
|
||||
{"type":"relation","to":"data_models","from":"schema_definitions","relationType":"documents"}
|
||||
{"type":"relation","to":"test_driven_development","from":"test_evolution","relationType":"describes"}
|
||||
{"type":"relation","to":"architecture_evolution","from":"implementation_challenges","relationType":"influenced"}
|
||||
{"type":"relation","to":"test_evolution","from":"implementation_challenges","relationType":"shaped"}
|
||||
{"type":"relation","to":"future_work","from":"implementation_challenges","relationType":"informs"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"Basic_Machines","relationType":"implements"}
|
||||
{"type":"relation","from":"Basic_Factory_Components","to":"Basic_Factory","relationType":"is_part_of"}
|
||||
{"type":"relation","from":"Component_Translation_Process","to":"Basic_Factory_Components","relationType":"enables"}
|
||||
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic_Machines","relationType":"guides"}
|
||||
{"type":"relation","from":"Paul","to":"Basic_Factory","relationType":"develops"}
|
||||
{"type":"relation","from":"Paul","to":"Basic_Machines_Philosophy","relationType":"created"}
|
||||
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Basic_Machines_Philosophy","relationType":"articulates"}
|
||||
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"Basic_Factory","relationType":"guides_development_of"}
|
||||
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Paul","relationType":"written_by"}
|
||||
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Component_Translation_Process","relationType":"documents"}
|
||||
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"Basic_Machines","relationType":"shapes_development_of"}
|
||||
{"type":"relation","from":"Basic_Machines_Roadmap","to":"Basic_Machines","relationType":"guides_development_of"}
|
||||
{"type":"relation","from":"Basic_Machines_Website","to":"Basic_Machines_Roadmap","relationType":"implements_phase_of"}
|
||||
{"type":"relation","from":"Basic_Factory_Components","to":"Basic_Machines_Website","relationType":"enables"}
|
||||
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic_Machines_Website","relationType":"informs"}
|
||||
{"type":"relation","from":"Paul","to":"DIY_Ethics","relationType":"embodies"}
|
||||
{"type":"relation","from":"Basic_Machines_Philosophy","to":"DIY_Ethics","relationType":"incorporates"}
|
||||
{"type":"relation","from":"Basic_Machines","to":"DIY_Ethics","relationType":"exemplifies"}
|
||||
{"type":"relation","from":"Component_Translation_Process","to":"Basic_Machines_Philosophy","relationType":"implements"}
|
||||
{"type":"relation","from":"Basic_Factory_Components","to":"DIY_Ethics","relationType":"demonstrates"}
|
||||
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"Basic_Machines_Philosophy","relationType":"aligns_with"}
|
||||
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"Component_Translation_Process","relationType":"guides"}
|
||||
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Basic_Machines","relationType":"defines_vision_for"}
|
||||
{"type":"relation","from":"Basic_Machines_Website","to":"Basic_Machines_Manifesto","relationType":"implements_vision_of"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"AI_Human_Collaboration_Model","relationType":"demonstrates"}
|
||||
{"type":"relation","from":"Paul","to":"AI_Human_Collaboration_Model","relationType":"developed_with_Claude"}
|
||||
{"type":"relation","from":"Basic_Factory_Components","to":"Component_Translation_Process","relationType":"created_through"}
|
||||
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic_Factory","relationType":"guides"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"MCP_Tools","relationType":"integrates"}
|
||||
{"type":"relation","from":"Basic_Machines_Website","to":"Basic_Factory_Components","relationType":"will_use"}
|
||||
{"type":"relation","from":"Basic_Machines_Roadmap","to":"Basic_Machines_Philosophy","relationType":"aligns_with"}
|
||||
{"type":"relation","from":"Component_Translation_Process","to":"MCP_Tools","relationType":"leverages"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"basic-memory","relationType":"will_document_process_in"}
|
||||
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"basic-memory","relationType":"will_be_implemented_in"}
|
||||
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic_Machines_Roadmap","relationType":"informs_priorities_of"}
|
||||
{"type":"relation","from":"basic-memory","to":"Basic_Machines_Philosophy","relationType":"embodies"}
|
||||
{"type":"relation","from":"Paul","to":"Basic_Machines_Manifesto","relationType":"authored_with_Claude"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"Component_Translation_Process","relationType":"validated"}
|
||||
{"type":"relation","from":"AI_Human_Collaboration_Model","to":"MCP_Tools","relationType":"utilizes"}
|
||||
{"type":"relation","from":"Basic_Factory_Components","to":"Basic_Machines_Roadmap","relationType":"supports"}
|
||||
{"type":"relation","from":"Basic_Machines_Website","to":"Basic_Factory","relationType":"will_demonstrate"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"basic-memory-webui","relationType":"enables_development_of"}
|
||||
{"type":"relation","from":"basic-memory","to":"AI_Human_Development_Methodology","relationType":"implements"}
|
||||
{"type":"relation","from":"Basic_Machines_Philosophy","to":"AI_Human_Development_Methodology","relationType":"guides"}
|
||||
{"type":"relation","from":"Basic_Factory_Components","to":"basic-memory-webui","relationType":"provides_ui_for"}
|
||||
{"type":"relation","from":"Component_Translation_Process","to":"AI_Human_Development_Methodology","relationType":"exemplifies"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"Basic Components","relationType":"enabled_creation_of"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"Tool Integration Discovery","relationType":"led_to"}
|
||||
{"type":"relation","from":"MCP_Integration_Progress","to":"AI_Human_Development_Methodology","relationType":"validates"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"MCP_Integration_Progress","relationType":"demonstrates"}
|
||||
{"type":"relation","from":"Basic_Factory","to":"AI_Human_Development_Methodology","relationType":"proves_effectiveness_of"}
|
||||
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Basic Components","relationType":"inspires_architecture_of"}
|
||||
{"type":"relation","from":"DIY_Ethics","to":"basic-memory","relationType":"shapes_design_of"}
|
||||
{"type":"relation","from":"Basic_Machines_Philosophy","to":"Tool Integration Discovery","relationType":"guides_analysis_of"}
|
||||
{"type":"relation","from":"Basic_Machines_Manifesto","to":"AI_Human_Development_Methodology","relationType":"documents_approach_of"}
|
||||
{"type":"relation","from":"Basic_Machines_Manifesto","to":"Basic_Factory_Components","relationType":"explains_principles_of"}
|
||||
{"type":"relation","from":"Basic_Memory_Project_Structure","to":"basic-memory","relationType":"organizes"}
|
||||
{"type":"relation","from":"Basic_Memory_Database_Schema","to":"basic-memory","relationType":"defines_storage_for"}
|
||||
{"type":"relation","from":"Basic_Memory_Markdown_Example","to":"Basic_Memory_File_Format","relationType":"demonstrates"}
|
||||
{"type":"relation","from":"Basic_Memory_Project_Isolation_Decision","to":"Basic_Memory_Future_Enhancement_Weighted_Relations","relationType":"similar_to"}
|
||||
{"type":"relation","to":"DIY_Ethics","from":"Basic_Memory_Project_Isolation_Decision","relationType":"follows"}
|
||||
{"type":"relation","from":"Basic_Memory_Implementation_Plan","to":"basic-memory","relationType":"guides"}
|
||||
{"type":"relation","from":"Basic_Memory_Implementation_Plan","to":"DIY_Ethics","relationType":"follows"}
|
||||
{"type":"relation","from":"Basic_Memory_Implementation_Plan","to":"Basic_Memory_Database_Schema","relationType":"implements"}
|
||||
{"type":"relation","from":"Basic_Memory_Implementation_Status","to":"Basic_Memory_Implementation_Plan","relationType":"updates"}
|
||||
{"type":"relation","from":"Basic_Memory_Observation_Management_Design","to":"Basic_Memory_Technical_Design","relationType":"extends"}
|
||||
{"type":"relation","from":"Basic_Memory_Architectural_Decisions","to":"DIY_Ethics","relationType":"guided_by"}
|
||||
{"type":"relation","from":"Basic_Memory_Architectural_Decisions","to":"basic-memory","relationType":"structures"}
|
||||
{"type":"relation","from":"Basic_Memory_Implementation_Status","to":"basic-memory","relationType":"describes_state_of"}
|
||||
{"type":"relation","to":"Basic_Memory_Implementation_Status","from":"Basic_Memory_Implementation_Analysis","relationType":"analyzes"}
|
||||
{"type":"relation","to":"basic-memory","from":"Basic_Memory_Current_Challenges","relationType":"identifies_issues_in"}
|
||||
{"type":"relation","to":"DIY_Ethics","from":"Basic_Memory_Implementation_Analysis","relationType":"confirms_alignment_with"}
|
||||
{"type":"relation","to":"Basic_Memory_Observation_Management_Design","from":"Basic_Memory_Observation_Hash_Tracking","relationType":"solves"}
|
||||
{"type":"relation","to":"DIY_Ethics","from":"Basic_Memory_Observation_Hash_Tracking","relationType":"aligns_with"}
|
||||
{"type":"relation","to":"Basic_Memory_File_Format","from":"Basic_Memory_Observation_Hash_Tracking","relationType":"preserves"}
|
||||
{"type":"relation","to":"Basic_Memory_Technical_Design","from":"Basic_Memory_Observation_Hash_Tracking","relationType":"enhances"}
|
||||
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"basic-memory","relationType":"implements_part_of"}
|
||||
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"Basic_Memory_Database_Schema","relationType":"follows"}
|
||||
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"DIY_Ethics","relationType":"aligns_with"}
|
||||
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"testing_infrastructure","relationType":"demonstrates"}
|
||||
{"type":"relation","from":"Basic_Memory_Repository_Implementation","to":"Basic Foundation","relationType":"inspired_by"}
|
||||
{"type":"relation","from":"Basic_Memory_Dependencies","to":"basic-memory","relationType":"supports"}
|
||||
{"type":"relation","from":"Basic_Memory_Dependencies","to":"Basic_Memory_Repository_Implementation","relationType":"enables"}
|
||||
{"type":"relation","from":"Basic_Memory_Dependencies","to":"testing_infrastructure","relationType":"enables"}
|
||||
{"type":"relation","from":"Basic_Memory_Current_Architecture","to":"basic-memory","relationType":"describes_state_of"}
|
||||
{"type":"relation","from":"Basic_Memory_Evolution","to":"Basic_Memory_Current_Architecture","relationType":"explains_development_of"}
|
||||
{"type":"relation","from":"Basic_Memory_Service_Layer","to":"Basic_Memory_Current_Architecture","relationType":"implements"}
|
||||
{"type":"relation","from":"Basic_Memory_Schema_Design","to":"Basic_Memory_Current_Architecture","relationType":"implements"}
|
||||
{"type":"relation","from":"Basic_Memory_Evolution","to":"Basic_Memory_Implementation_Plan","relationType":"reflects_on"}
|
||||
{"type":"relation","from":"Basic_Memory_Evolution","to":"DIY_Ethics","relationType":"demonstrates_alignment_with"}
|
||||
{"type":"relation","from":"Basic_Memory_Current_Architecture","to":"DIY_Ethics","relationType":"embodies"}
|
||||
{"type":"relation","from":"Basic_Memory_Service_Layer","to":"fileio_module","relationType":"uses"}
|
||||
{"type":"relation","from":"Basic_Memory_Schema_Design","to":"markdown_format","relationType":"implements"}
|
||||
{"type":"relation","to":"basic-memory","from":"Basic_Memory_Next_Tasks","relationType":"guides_development_of"}
|
||||
{"type":"relation","to":"DIY_Ethics","from":"Basic_Memory_Next_Tasks","relationType":"aligns_with"}
|
||||
{"type":"relation","to":"Basic_Memory_Current_Architecture","from":"Basic_Memory_Next_Tasks","relationType":"extends"}
|
||||
{"type":"relation","from":"Basic_Memory_Meta_Experience","to":"basic-memory","relationType":"validates_design_of"}
|
||||
{"type":"relation","from":"Basic_Memory_Meta_Experience","to":"DIY_Ethics","relationType":"demonstrates_principles_of"}
|
||||
{"type":"relation","from":"Basic_Memory_Meta_Experience","to":"design_decisions","relationType":"reinforces"}
|
||||
{"type":"relation","from":"Basic_Memory_Meta_Experience","to":"Basic_Memory_Current_Architecture","relationType":"validates"}
|
||||
{"type":"relation","from":"Model_Context_Protocol","to":"basic-memory","relationType":"enables"}
|
||||
{"type":"relation","from":"basic-memory_core_principles","to":"basic-memory","relationType":"guides"}
|
||||
{"type":"relation","from":"basic-memory_core_principles","to":"DIY_Ethics","relationType":"aligns_with"}
|
||||
{"type":"relation","from":"basic-memory_business_model","to":"basic-memory","relationType":"defines_sustainability_for"}
|
||||
{"type":"relation","from":"basic-memory_business_model","to":"DIY_Ethics","relationType":"maintains_alignment_with"}
|
||||
{"type":"relation","from":"basic-memory_cli","to":"basic-memory","relationType":"provides_interface_for"}
|
||||
{"type":"relation","from":"basic-memory_cli","to":"Model_Context_Protocol","relationType":"integrates_with"}
|
||||
{"type":"relation","from":"basic-memory_export_format","to":"basic-memory","relationType":"standardizes_output_of"}
|
||||
{"type":"relation","from":"basic-memory_export_format","to":"markdown_format","relationType":"extends"}
|
||||
{"type":"relation","from":"basic-memory_core_principles","to":"Basic_Machines_Philosophy","relationType":"implements"}
|
||||
{"type":"relation","from":"Model_Context_Protocol","to":"AI_Human_Collaboration_Model","relationType":"enables"}
|
||||
{"type":"relation","from":"relation_service","to":"basic-memory","relationType":"will_be_component_of"}
|
||||
{"type":"relation","from":"relation_service","to":"service_layer_patterns","relationType":"follows"}
|
||||
{"type":"relation","from":"relation_service","to":"fileio_patterns","relationType":"uses"}
|
||||
{"type":"relation","from":"relation_service","to":"database_models","relationType":"uses"}
|
||||
{"type":"relation","from":"relation_service","to":"repository_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"relation_service_design","to":"relation_service","relationType":"guides_implementation_of"}
|
||||
{"type":"relation","from":"relation_service_implementation_plan","to":"relation_service","relationType":"defines_implementation_of"}
|
||||
{"type":"relation","from":"relation_service_challenges","to":"relation_service_design","relationType":"informs"}
|
||||
{"type":"relation","from":"relation_file_format","to":"markdown_format","relationType":"extends"}
|
||||
{"type":"relation","from":"relation_service_error_handling","to":"service_layer_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"relation_service_testing","to":"testing_infrastructure","relationType":"extends"}
|
||||
{"type":"relation","from":"fileio_patterns","to":"service_layer_patterns","relationType":"enables"}
|
||||
{"type":"relation","from":"database_models","to":"repository_patterns","relationType":"enables"}
|
||||
{"type":"relation","from":"relation_service","to":"entity_service","relationType":"coordinates_with"}
|
||||
{"type":"relation","from":"relation_file_format","to":"relation_service","relationType":"defines_storage_for"}
|
||||
{"type":"relation","from":"relation_service_error_handling","to":"relation_service","relationType":"ensures_reliability_of"}
|
||||
{"type":"relation","from":"relation_service_testing","to":"relation_service","relationType":"verifies"}
|
||||
{"type":"relation","from":"service_layer_patterns","to":"basic-memory_implementation_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"repository_patterns","to":"basic-memory_implementation_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"fileio_patterns","to":"basic-memory_implementation_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"database_models","to":"basic-memory_implementation_patterns","relationType":"implements"}
|
||||
{"type":"relation","from":"relation_service_challenges","to":"implementation_challenges","relationType":"extends"}
|
||||
{"type":"relation","from":"relation_service_implementation_plan","to":"future_work","relationType":"details"}
|
||||
{"type":"relation","from":"relation_service_design","to":"design_decisions","relationType":"aligns_with"}
|
||||
{"type":"relation","from":"relation_file_format","to":"design_decisions","relationType":"follows"}
|
||||
{"type":"relation","from":"pytest_patterns","to":"testing_infrastructure","relationType":"extends"}
|
||||
{"type":"relation","from":"relation_implementation_learnings","to":"basic-memory_implementation_patterns","relationType":"informs"}
|
||||
{"type":"relation","from":"test_driven_insights","to":"test_driven_development","relationType":"enriches"}
|
||||
{"type":"relation","from":"meta_development_insights","to":"AI_Human_Collaboration_Model","relationType":"improves"}
|
||||
{"type":"relation","from":"relation_implementation_learnings","to":"relation_service","relationType":"guides_implementation_of"}
|
||||
{"type":"relation","from":"pytest_patterns","to":"test_evolution","relationType":"demonstrates"}
|
||||
{"type":"relation","from":"test_driven_insights","to":"design_decisions","relationType":"influences"}
|
||||
{"type":"relation","from":"meta_development_insights","to":"architecture_evolution","relationType":"informs"}
|
||||
{"type":"relation","from":"relation_service","to":"relation_implementation_learnings","relationType":"validates"}
|
||||
{"type":"relation","from":"test_driven_insights","to":"implementation_challenges","relationType":"helps_solve"}
|
||||
{"type":"relation","from":"AI_Assistant_Learnings","to":"meta_development_insights","relationType":"enriches"}
|
||||
{"type":"relation","from":"Effective_Response_Patterns","to":"AI_Assistant_Learnings","relationType":"implements"}
|
||||
{"type":"relation","from":"AI_Context_Management","to":"AI_Human_Collaboration_Model","relationType":"improves"}
|
||||
{"type":"relation","from":"AI_Tool_Usage_Patterns","to":"AI_Context_Management","relationType":"enables"}
|
||||
{"type":"relation","from":"AI_Assistant_Learnings","to":"Basic_Memory_Meta_Experience","relationType":"validates"}
|
||||
{"type":"relation","from":"AI_Tool_Usage_Patterns","to":"Model_Context_Protocol","relationType":"demonstrates_effective_use_of"}
|
||||
{"type":"relation","from":"AI_Context_Management","to":"basic-memory","relationType":"validates_design_of"}
|
||||
{"type":"relation","from":"Effective_Response_Patterns","to":"AI_Human_Development_Methodology","relationType":"refines"}
|
||||
{"type":"relation","to":"relation_service","from":"relation_service_patterns","relationType":"guides"}
|
||||
{"type":"relation","to":"test_driven_development","from":"test_driven_insights_relations","relationType":"enriches"}
|
||||
{"type":"relation","to":"implementation_challenges","from":"relation_service_learnings","relationType":"solves"}
|
||||
{"type":"relation","to":"basic-memory_implementation_patterns","from":"relation_service_patterns","relationType":"implements"}
|
||||
{"type":"relation","to":"markdown_format","from":"relation_service_patterns","relationType":"extends"}
|
||||
{"type":"relation","to":"service_layer_patterns","from":"relation_service_patterns","relationType":"refines"}
|
||||
{"type":"relation","from":"packaging_learnings","to":"implementation_challenges","relationType":"informs"}
|
||||
{"type":"relation","from":"packaging_learnings","to":"test_driven_development","relationType":"impacts"}
|
||||
{"type":"relation","to":"basic-memory","from":"Recent_Implementation_Progress","relationType":"updates_status_of"}
|
||||
{"type":"relation","to":"future_work","from":"Next_Steps","relationType":"extends"}
|
||||
{"type":"relation","to":"design_decisions","from":"Development_Practices","relationType":"informs"}
|
||||
{"type":"relation","to":"packaging_learnings","from":"Development_Practices","relationType":"incorporates"}
|
||||
{"type":"relation","to":"test_driven_development","from":"Development_Practices","relationType":"refines"}
|
||||
{"type":"relation","to":"basic-memory_implementation_patterns","from":"Development_Practices","relationType":"enhances"}
|
||||
{"type":"relation","from":"Basic_Memory_MCP","to":"MCP_Server_Implementation","relationType":"follows"}
|
||||
{"type":"relation","from":"Basic_Memory_MCP","to":"MCP_Tools","relationType":"uses"}
|
||||
{"type":"relation","from":"Basic_Memory","to":"MCP_Server_Implementation","relationType":"implements"}
|
||||
{"type":"relation","from":"Basic_Memory_Testing","to":"Memory_Service_Tests","relationType":"includes"}
|
||||
{"type":"relation","from":"Basic_Memory_Testing","to":"MCP_Server_Tests","relationType":"includes"}
|
||||
{"type":"relation","from":"Memory_Service_Tests","to":"Basic_Memory_MCP","relationType":"validates"}
|
||||
{"type":"relation","from":"MCP_Server_Tests","to":"Basic_Memory_MCP","relationType":"validates"}
|
||||
{"type":"relation","from":"Service_Interface_Audit","to":"Memory_Service_Refactoring","relationType":"informs"}
|
||||
{"type":"relation","from":"Memory_Service_Refactoring","to":"Basic_Memory_MCP","relationType":"affects"}
|
||||
{"type":"relation","from":"Memory_Service_Patterns","to":"Basic_Memory_MCP","relationType":"improves"}
|
||||
{"type":"relation","from":"Pydantic_Create_Pattern","to":"Memory_Service_Patterns","relationType":"enables"}
|
||||
{"type":"relation","from":"Pydantic_Create_Pattern","to":"Basic_Memory_MCP","relationType":"improves"}
|
||||
{"type":"relation","from":"MCP_Marketplace","to":"Basic_Memory_Business","relationType":"enables"}
|
||||
{"type":"relation","from":"Basic_Memory","to":"MCP_Marketplace","relationType":"could_integrate_with"}
|
||||
{"type":"relation","from":"Persistence_Of_Vision","to":"Basic_Memory","relationType":"helps_achieve"}
|
||||
{"type":"relation","from":"Drew","to":"Persistence_Of_Vision","relationType":"conceptualized"}
|
||||
{"type":"relation","to":"Usage_Recipes","from":"Conversation_Continuity_Pattern","relationType":"is_example_of"}
|
||||
{"type":"relation","to":"Basic_Memory","from":"Usage_Recipes","relationType":"enhances"}
|
||||
{"type":"relation","to":"Basic_Memory","from":"Chat_References","relationType":"enhances"}
|
||||
{"type":"relation","to":"Conversation_Continuity_Pattern","from":"Chat_References","relationType":"implements"}
|
||||
{"type":"relation","from":"20240307-chat-reference-protocol-test","to":"20240307-chat-reference-protocol","relationType":"continues_from"}
|
||||
{"type":"relation","from_id":"Run_Tests_Tool_Request","to_id":"Basic_Machines","relationType":"enhances","context":"development workflow improvement"}
|
||||
{"type":"relation","from_id":"SQLAlchemy_Async_Loading_Pattern","to_id":"basic-memory","relation_type":"improves","context":"database performance and async compatibility"}
|
||||
{"type":"relation","from_id":"SQLAlchemy_Async_Loading_Pattern","to_id":"Entity","relation_type":"applies_to","context":"relationship loading strategy"}
|
||||
{"type":"relation","from":"MCP_Reference_Integration","to":"Project_Priorities","relationType":"prioritized_after"}
|
||||
{"type":"relation","from":"great_observation_loading_saga_20241207","to":"Basic_Memory","relationType":"occurred_in"}
|
||||
{"type":"relation","from":"great_observation_loading_saga_20241207","to":"SQLAlchemy","relationType":"relates_to"}
|
||||
{"type":"relation","from":"basic_memory_implementation_20241208","to":"Basic_Memory","relationType":"improves"}
|
||||
{"type":"relation","from":"great_observation_loading_saga_20241207","to":"basic_memory_implementation_20241208","relationType":"leads_to"}
|
||||
{"type":"relation","from":"MCP_Dependency_Risk","to":"DIY_Ethics","relationType":"validates"}
|
||||
{"type":"relation","from":"MCP_Dependency_Risk","to":"basic-memory_core_principles","relationType":"reinforces"}
|
||||
{"type":"relation","from":"MCP_Dependency_Risk","to":"Basic_Memory_Implementation_Plan","relationType":"influences"}
|
||||
{"type":"relation","from":"basic_memory_mcp_architecture","to":"basic_memory_project_20241208","relationType":"implements"}
|
||||
{"type":"relation","from":"basic_memory_sync_considerations","to":"basic_memory_project_20241208","relationType":"influences"}
|
||||
{"type":"relation","from":"mcp_server_learnings","to":"basic_memory_mcp_architecture","relationType":"informs"}
|
||||
{"type":"relation","from":"20241208-mcp-tool-refactoring","to":"Basic_Memory_MCP","relationType":"improves"}
|
||||
{"type":"relation","from":"20241208-mcp-tool-refactoring","to":"Basic_Memory_Implementation_Plan","relationType":"implements"}
|
||||
@@ -3,7 +3,7 @@ name = "basic-memory"
|
||||
dynamic = ["version"]
|
||||
description = "Local-first knowledge management combining Zettelkasten with knowledge graphs"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12.1"
|
||||
requires-python = ">=3.12"
|
||||
license = { text = "AGPL-3.0-or-later" }
|
||||
authors = [
|
||||
{ name = "Basic Machines", email = "hello@basic-machines.co" }
|
||||
@@ -15,7 +15,6 @@ dependencies = [
|
||||
"aiosqlite>=0.20.0",
|
||||
"greenlet>=3.1.1",
|
||||
"pydantic[email,timezone]>=2.10.3",
|
||||
"icecream>=2.1.3",
|
||||
"mcp>=1.2.0",
|
||||
"pydantic-settings>=2.6.1",
|
||||
"loguru>=0.7.3",
|
||||
@@ -30,10 +29,15 @@ dependencies = [
|
||||
"alembic>=1.14.1",
|
||||
"pillow>=11.1.0",
|
||||
"pybars3>=0.9.7",
|
||||
"fastmcp>=2.3.4",
|
||||
"fastmcp==2.12.3", # Pinned - 2.14.x breaks MCP tools visibility (issue #463)
|
||||
"pyjwt>=2.10.1",
|
||||
"python-dotenv>=1.1.0",
|
||||
"pytest-aio>=1.9.0",
|
||||
"aiofiles>=24.1.0", # Optional observability (disabled by default via config)
|
||||
"asyncpg>=0.30.0",
|
||||
"nest-asyncio>=1.6.0", # For Alembic migrations with Postgres
|
||||
"pytest-asyncio>=1.2.0",
|
||||
"psycopg==3.3.1",
|
||||
]
|
||||
|
||||
|
||||
@@ -52,17 +56,23 @@ build-backend = "hatchling.build"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
pythonpath = ["src", "tests"]
|
||||
addopts = "--cov=basic_memory --cov-report term-missing -ra -q"
|
||||
testpaths = ["tests"]
|
||||
addopts = "--cov=basic_memory --cov-report term-missing"
|
||||
testpaths = ["tests", "test-int"]
|
||||
asyncio_mode = "strict"
|
||||
asyncio_default_fixture_loop_scope = "function"
|
||||
markers = [
|
||||
"benchmark: Performance benchmark tests (deselect with '-m \"not benchmark\"')",
|
||||
"slow: Slow-running tests (deselect with '-m \"not slow\"')",
|
||||
"postgres: Tests that run against Postgres backend (deselect with '-m \"not postgres\"')",
|
||||
"windows: Windows-specific tests (deselect with '-m \"not windows\"')",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
target-version = "py312"
|
||||
|
||||
[tool.uv]
|
||||
dev-dependencies = [
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"gevent>=24.11.1",
|
||||
"icecream>=2.1.3",
|
||||
"pytest>=8.3.4",
|
||||
@@ -71,6 +81,9 @@ dev-dependencies = [
|
||||
"pytest-asyncio>=0.24.0",
|
||||
"pytest-xdist>=3.0.0",
|
||||
"ruff>=0.1.6",
|
||||
"freezegun>=1.5.5",
|
||||
"testcontainers[postgres]>=4.0.0",
|
||||
"psycopg>=3.2.0",
|
||||
]
|
||||
|
||||
[tool.hatch.version]
|
||||
@@ -123,4 +136,4 @@ omit = [
|
||||
]
|
||||
|
||||
[tool.logfire]
|
||||
ignore_no_config = true
|
||||
ignore_no_config = true
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Welcome to Basic Memory installer"
|
||||
|
||||
# 1. Install uv if not present
|
||||
if ! command -v uv &> /dev/null; then
|
||||
echo "Installing uv package manager..."
|
||||
curl -LsSf https://github.com/astral-sh/uv/releases/download/0.1.23/uv-installer.sh | sh
|
||||
fi
|
||||
|
||||
# 2. Configure Claude Desktop
|
||||
echo "Configuring Claude Desktop..."
|
||||
CONFIG_FILE="$HOME/Library/Application Support/Claude/claude_desktop_config.json"
|
||||
|
||||
# Create config directory if it doesn't exist
|
||||
mkdir -p "$(dirname "$CONFIG_FILE")"
|
||||
|
||||
# If config file doesn't exist, create it with initial structure
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo '{"mcpServers": {}}' > "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
# Add/update the basic-memory config using jq
|
||||
jq '.mcpServers."basic-memory" = {
|
||||
"command": "uvx",
|
||||
"args": ["basic-memory"]
|
||||
}' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
|
||||
|
||||
echo "Installation complete! Basic Memory is now available in Claude Desktop."
|
||||
echo "Please restart Claude Desktop for changes to take effect."
|
||||
|
||||
echo -e "\nQuick Start:"
|
||||
echo "1. You can run sync directly using: uvx basic-memory sync"
|
||||
echo "2. Optionally, install globally with: uv pip install basic-memory"
|
||||
echo -e "\nBuilt with ♥️ by Basic Machines."
|
||||
@@ -0,0 +1,156 @@
|
||||
---
|
||||
title: 'SPEC-1: Specification-Driven Development Process'
|
||||
type: spec
|
||||
permalink: specs/spec-1-specification-driven-development-process
|
||||
tags:
|
||||
- process
|
||||
- specification
|
||||
- development
|
||||
- meta
|
||||
---
|
||||
|
||||
# SPEC-1: Specification-Driven Development Process
|
||||
|
||||
## Why
|
||||
We're implementing specification-driven development to solve the complexity and circular refactoring issues in our web development process.
|
||||
Instead of getting lost in framework details and type gymnastics, we start with clear specifications that drive implementation.
|
||||
|
||||
The default approach of adhoc development with AI agents tends to result in:
|
||||
- Circular refactoring cycles
|
||||
- Fighting framework complexity
|
||||
- Lost context between sessions
|
||||
- Unclear requirements and scope
|
||||
|
||||
## What
|
||||
This spec defines our process for using basic-memory as the specification engine to build basic-memory-cloud.
|
||||
We're creating a recursive development pattern where basic-memory manages the specs that drive the development of basic-memory-cloud.
|
||||
|
||||
**Affected Areas:**
|
||||
- All future component development
|
||||
- Architecture decisions
|
||||
- Agent collaboration workflows
|
||||
- Knowledge management and context preservation
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### Specification Structure
|
||||
|
||||
Name: Spec names should be numbered sequentially, followed by a description eg. `SPEC-X - Simple Description.md`.
|
||||
See: [[Spec-2: Slash Commands Reference]]
|
||||
|
||||
Every spec is a complete thought containing:
|
||||
- **Why**: The reasoning and problem being solved
|
||||
- **What**: What is affected or changed
|
||||
- **How**: High-level approach to implementation
|
||||
- **How to Evaluate**: Testing/validation procedure
|
||||
- Additional context as needed
|
||||
|
||||
### Living Specification Format
|
||||
|
||||
Specifications are **living documents** that evolve throughout implementation:
|
||||
|
||||
**Progress Tracking:**
|
||||
- **Completed items**: Use ✅ checkmark emoji for implemented features
|
||||
- **Pending items**: Use `- [ ]` GitHub-style checkboxes for remaining tasks
|
||||
- **In-progress items**: Use `- [x]` when work is actively underway
|
||||
|
||||
**Status Philosophy:**
|
||||
- **Avoid static status headers** like "COMPLETE" or "IN PROGRESS" that become stale
|
||||
- **Use checklists within content** to show granular implementation progress
|
||||
- **Keep specs informative** while providing clear progress visibility
|
||||
- **Update continuously** as understanding and implementation evolve
|
||||
|
||||
**Example Format:**
|
||||
```markdown
|
||||
### ComponentName
|
||||
- ✅ Basic functionality implemented
|
||||
- ✅ Props and events defined
|
||||
- - [ ] Add sorting controls
|
||||
- - [ ] Improve accessibility
|
||||
- - [x] Currently implementing responsive design
|
||||
```
|
||||
|
||||
This creates **git-friendly progress tracking** where `[ ]` easily becomes `[x]` or ✅ when completed, and specs remain valuable throughout the development lifecycle.
|
||||
|
||||
|
||||
## Claude Code
|
||||
|
||||
We will leverage Claude Code capabilities to make the process semi-automated.
|
||||
|
||||
- Slash commands: define repeatable steps in the process (create spec, implement, review, etc)
|
||||
- Agents: define roles to carry out instructions (front end developer, baskend developer, etc)
|
||||
- MCP tools: enable agents to implement specs via actions (write code, test, etc)
|
||||
|
||||
### Workflow
|
||||
1. **Create**: Write spec as complete thought in `/specs` folder
|
||||
2. **Discuss**: Iterate and refine through agent collaboration
|
||||
3. **Implement**: Hand spec to appropriate specialist agent
|
||||
4. **Validate**: Review implementation against spec criteria
|
||||
5. **Document**: Update spec with learnings and decisions
|
||||
|
||||
### Slash Commands
|
||||
|
||||
Claude slash commands are used to manage the flow.
|
||||
These are simple instructions to help make the process uniform.
|
||||
They can be updated and refined as needed.
|
||||
|
||||
- `/spec create [name]` - Create new specification
|
||||
- `/spec status` - Show current spec states
|
||||
- `/spec implement [name]` - Hand to appropriate agent
|
||||
- `/spec review [name]` - Validate implementation
|
||||
|
||||
### Agent Orchestration
|
||||
|
||||
Agents are defined with clear roles, for instance:
|
||||
|
||||
- **system-architect**: Creates high-level specs, ADRs, architectural decisions
|
||||
- **vue-developer**: Component specs, UI patterns, frontend architecture
|
||||
- **python-developer**: Implementation specs, technical details, backend logic
|
||||
-
|
||||
- Each agent reads/updates specs through basic-memory tools.
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
- Specs provide clear, actionable guidance for implementation
|
||||
- Reduced circular refactoring and scope creep
|
||||
- Persistent context across development sessions
|
||||
- Clean separation between "what/why" and implementation details
|
||||
- Specs record a history of what happened and why for historical context
|
||||
|
||||
### Testing Procedure
|
||||
1. Create a spec for an existing problematic component
|
||||
2. Have an agent implement following only the spec
|
||||
3. Compare result quality and development speed vs. ad-hoc approach
|
||||
4. Measure context preservation across sessions
|
||||
5. Evaluate spec clarity and completeness
|
||||
|
||||
### Metrics
|
||||
- Time from spec to working implementation
|
||||
- Number of refactoring cycles required
|
||||
- Agent understanding of requirements
|
||||
- Spec reusability for similar components
|
||||
|
||||
## Notes
|
||||
- Start simple: specs are just complete thoughts, not heavy processes
|
||||
- Use basic-memory's knowledge graph to link specs, decisions, components
|
||||
- Let the process evolve naturally based on what works
|
||||
- Focus on solving the actual problem: Manage complexity in development
|
||||
|
||||
## Observations
|
||||
|
||||
- [problem] Web development without clear goals and documentation circular refactoring cycles #complexity
|
||||
- [solution] Specification-driven development reduces scope creep and context loss #process-improvement
|
||||
- [pattern] basic-memory as specification engine creates recursive development loop #meta-development
|
||||
- [workflow] Five-step process: Create → Discuss → Implement → Validate → Document #methodology
|
||||
- [tool] Slash commands provide uniform process automation #automation
|
||||
- [agent-pattern] Three specialized agents handle different implementation domains #specialization
|
||||
- [success-metric] Time from spec to working implementation measures process efficiency #measurement
|
||||
- [learning] Process should evolve naturally based on what works in practice #adaptation
|
||||
- [format] Living specifications use checklists for progress tracking instead of static status headers #documentation
|
||||
- [evolution] Specs evolve throughout implementation maintaining value as working documents #continuous-improvement
|
||||
|
||||
## Relations
|
||||
|
||||
- spec [[Spec-2: Slash Commands Reference]]
|
||||
- spec [[Spec-3: Agent Definitions]]
|
||||
@@ -0,0 +1,569 @@
|
||||
---
|
||||
title: 'SPEC-10: Unified Deployment Workflow and Event Tracking'
|
||||
type: spec
|
||||
permalink: specs/spec-10-unified-deployment-workflow-event-tracking
|
||||
tags:
|
||||
- workflow
|
||||
- deployment
|
||||
- event-sourcing
|
||||
- architecture
|
||||
- simplification
|
||||
---
|
||||
|
||||
# SPEC-10: Unified Deployment Workflow and Event Tracking
|
||||
|
||||
## Why
|
||||
|
||||
We replaced a complex multi-workflow system with DBOS orchestration that was proving to be more trouble than it was worth. The previous architecture had four separate workflows (`tenant_provisioning`, `tenant_update`, `tenant_deployment`, `tenant_undeploy`) with overlapping logic, complex state management, and fragmented event tracking. DBOS added unnecessary complexity without providing sufficient value, leading to harder debugging and maintenance.
|
||||
|
||||
**Problems Solved:**
|
||||
- **Framework Complexity**: DBOS configuration overhead and fighting framework limitations
|
||||
- **Code Duplication**: Multiple workflows implementing similar operations with duplicate logic
|
||||
- **Poor Observability**: Fragmented event tracking across workflow boundaries
|
||||
- **Maintenance Overhead**: Complex orchestration for fundamentally simple operations
|
||||
- **Debugging Difficulty**: Framework abstractions hiding simple Python stack traces
|
||||
|
||||
## What
|
||||
|
||||
This spec documents the architectural simplification that consolidates tenant lifecycle management into a unified system with comprehensive event tracking.
|
||||
|
||||
**Affected Areas:**
|
||||
- Tenant deployment workflows (provisioning, updates, undeploying)
|
||||
- Event sourcing and workflow tracking infrastructure
|
||||
- API endpoints for tenant operations
|
||||
- Database schema for workflow and event correlation
|
||||
- Integration testing for tenant lifecycle operations
|
||||
|
||||
**Key Changes:**
|
||||
- **Removed DBOS entirely** - eliminated framework dependency and complexity
|
||||
- **Consolidated 4 workflows → 2 unified deployment workflows (deploy/undeploy)**
|
||||
- **Added workflow tracking system** with complete event correlation
|
||||
- **Simplified API surface** - single `/deploy` endpoint handles all scenarios
|
||||
- **Enhanced observability** through event sourcing with workflow grouping
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### Architectural Philosophy
|
||||
**Embrace simplicity over framework complexity** - use well-structured Python with proper database design instead of complex orchestration frameworks.
|
||||
|
||||
### Core Components
|
||||
|
||||
#### 1. Unified Deployment Workflow
|
||||
```python
|
||||
class TenantDeploymentWorkflow:
|
||||
async def deploy_tenant_workflow(self, tenant_id: str, workflow_id: UUID, image_tag: str = None):
|
||||
# Single workflow handles both initial provisioning AND updates
|
||||
# Each step is idempotent and handles its own error recovery
|
||||
# Database transactions provide the durability we need
|
||||
await self.start_deployment_step(workflow_id, tenant_uuid, image_tag)
|
||||
await self.create_fly_app_step(workflow_id, tenant_uuid)
|
||||
await self.create_bucket_step(workflow_id, tenant_uuid)
|
||||
await self.deploy_machine_step(workflow_id, tenant_uuid, image_tag)
|
||||
await self.complete_deployment_step(workflow_id, tenant_uuid, image_tag, deployment_time)
|
||||
```
|
||||
|
||||
**Key Benefits:**
|
||||
- **Handles both provisioning and updates** in single workflow
|
||||
- **Idempotent operations** - safe to retry any step
|
||||
- **Clean error handling** via simple Python exceptions
|
||||
- **Resumable** - can restart from any failed step
|
||||
|
||||
#### 2. Workflow Tracking System
|
||||
|
||||
**Database Schema:**
|
||||
```sql
|
||||
CREATE TABLE workflow (
|
||||
id UUID PRIMARY KEY,
|
||||
workflow_type VARCHAR(50) NOT NULL, -- 'tenant_deployment', 'tenant_undeploy'
|
||||
tenant_id UUID REFERENCES tenant(id),
|
||||
status VARCHAR(20) DEFAULT 'running', -- 'running', 'completed', 'failed'
|
||||
workflow_metadata JSONB DEFAULT '{}' -- image_tag, etc.
|
||||
);
|
||||
|
||||
ALTER TABLE event ADD COLUMN workflow_id UUID REFERENCES workflow(id);
|
||||
```
|
||||
|
||||
**Event Correlation:**
|
||||
- Every workflow operation generates events tagged with `workflow_id`
|
||||
- Complete audit trail from workflow start to completion
|
||||
- Events grouped by workflow for easy reconstruction of operations
|
||||
|
||||
#### 3. Parameter Standardization
|
||||
All workflow methods follow consistent signature pattern:
|
||||
```python
|
||||
async def method_name(self, session: AsyncSession, workflow_id: UUID | None, tenant_id: UUID, ...)
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- **Consistent event tagging** - all events properly correlated
|
||||
- **Clear method contracts** - workflow_id always first parameter
|
||||
- **Type safety** - proper UUID handling throughout
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
#### Phase 1: Workflow Consolidation ✅ COMPLETED
|
||||
- [x] **Remove DBOS dependency** - eliminated dbos_config.py and all DBOS imports
|
||||
- [x] **Create unified TenantDeploymentWorkflow** - handles both provisioning and updates
|
||||
- [x] **Remove legacy workflows** - deleted tenant_provisioning.py, tenant_update.py
|
||||
- [x] **Simplify API endpoints** - consolidated to single `/deploy` endpoint
|
||||
- [x] **Update integration tests** - comprehensive edge case testing
|
||||
|
||||
#### Phase 2: Workflow Tracking System ✅ COMPLETED
|
||||
- [x] **Database migration** - added workflow table and event.workflow_id foreign key
|
||||
- [x] **Workflow repository** - CRUD operations for workflow records
|
||||
- [x] **Event correlation** - all workflow events tagged with workflow_id
|
||||
- [x] **Comprehensive testing** - workflow lifecycle and event grouping tests
|
||||
|
||||
#### Phase 3: Parameter Standardization ✅ COMPLETED
|
||||
- [x] **Standardize method signatures** - workflow_id as first parameter pattern
|
||||
- [x] **Fix event tagging** - ensure all workflow events properly correlated
|
||||
- [x] **Update service methods** - consistent parameter order across tenant_service
|
||||
- [x] **Integration test validation** - verify complete event sequences
|
||||
|
||||
### Architectural Benefits
|
||||
|
||||
#### Code Simplification
|
||||
- **39 files changed**: 2,247 additions, 3,256 deletions (net -1,009 lines)
|
||||
- **Eliminated framework complexity** - no more DBOS configuration or abstractions
|
||||
- **Consolidated logic** - single deployment workflow vs 4 separate workflows
|
||||
- **Cleaner API surface** - unified endpoint vs multiple workflow-specific endpoints
|
||||
|
||||
#### Enhanced Observability
|
||||
- **Complete event correlation** - every workflow event tagged with workflow_id
|
||||
- **Audit trail reconstruction** - can trace entire tenant lifecycle through events
|
||||
- **Workflow status tracking** - running/completed/failed states in database
|
||||
- **Comprehensive testing** - edge cases covered with real infrastructure
|
||||
|
||||
#### Operational Benefits
|
||||
- **Simpler debugging** - plain Python stack traces vs framework abstractions
|
||||
- **Reduced dependencies** - one less complex framework to maintain
|
||||
- **Better error handling** - explicit exception handling vs framework magic
|
||||
- **Easier maintenance** - straightforward Python code vs orchestration complexity
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
|
||||
#### Functional Completeness ✅ VERIFIED
|
||||
- [x] **Unified deployment workflow** handles both initial provisioning and updates
|
||||
- [x] **Undeploy workflow** properly integrated with event tracking
|
||||
- [x] **All operations idempotent** - safe to retry any step without duplication
|
||||
- [x] **Complete tenant lifecycle** - provision → active → update → undeploy
|
||||
|
||||
#### Event Tracking and Correlation ✅ VERIFIED
|
||||
- [x] **All workflow events tagged** with proper workflow_id
|
||||
- [x] **Event sequence verification** - tests assert exact event order and content
|
||||
- [x] **Workflow grouping** - events can be queried by workflow_id for complete audit trail
|
||||
- [x] **Cross-workflow isolation** - deployment vs undeploy events properly separated
|
||||
|
||||
#### Database Schema and Performance ✅ VERIFIED
|
||||
- [x] **Migration applied** - workflow table and event.workflow_id column created
|
||||
- [x] **Proper indexing** - performance optimized queries on workflow_type, tenant_id, status
|
||||
- [x] **Foreign key constraints** - referential integrity between workflows and events
|
||||
- [x] **Database triggers** - updated_at timestamp automation
|
||||
|
||||
#### Test Coverage ✅ COMPREHENSIVE
|
||||
- [x] **Unit tests**: 4 workflow tracking tests covering lifecycle and event grouping
|
||||
- [x] **Integration tests**: Real infrastructure testing with Fly.io resources
|
||||
- [x] **Edge case coverage**: Failed deployments, partial state recovery, resource conflicts
|
||||
- [x] **Event sequence verification**: Exact event order and content validation
|
||||
|
||||
### Testing Procedure
|
||||
|
||||
#### Unit Test Validation ✅ PASSING
|
||||
```bash
|
||||
cd apps/cloud && pytest tests/test_workflow_tracking.py -v
|
||||
# 4/4 tests passing - workflow lifecycle and event grouping
|
||||
```
|
||||
|
||||
#### Integration Test Validation ✅ PASSING
|
||||
```bash
|
||||
cd apps/cloud && pytest tests/integration/test_tenant_workflow_deployment_integration.py -v
|
||||
cd apps/cloud && pytest tests/integration/test_tenant_workflow_undeploy_integration.py -v
|
||||
# Comprehensive real infrastructure testing with actual Fly.io resources
|
||||
# Tests provision → deploy → update → undeploy → cleanup cycles
|
||||
```
|
||||
|
||||
### Performance Metrics
|
||||
|
||||
#### Code Metrics ✅ ACHIEVED
|
||||
- **Net code reduction**: -1,009 lines (3,256 deletions, 2,247 additions)
|
||||
- **Workflow consolidation**: 4 workflows → 1 unified deployment workflow
|
||||
- **Dependency reduction**: Removed DBOS framework dependency entirely
|
||||
- **API simplification**: Multiple endpoints → single `/deploy` endpoint
|
||||
|
||||
#### Operational Metrics ✅ VERIFIED
|
||||
- **Event correlation**: 100% of workflow events properly tagged with workflow_id
|
||||
- **Audit trail completeness**: Full tenant lifecycle traceable through event sequences
|
||||
- **Error handling**: Clean Python exceptions vs framework abstractions
|
||||
- **Debugging simplicity**: Direct stack traces vs orchestration complexity
|
||||
|
||||
### Implementation Status: ✅ COMPLETE
|
||||
|
||||
All phases completed successfully with comprehensive testing and verification:
|
||||
|
||||
**Phase 1 - Workflow Consolidation**: ✅ COMPLETE
|
||||
- Removed DBOS dependency and consolidated workflows
|
||||
- Unified deployment workflow handles all scenarios
|
||||
- Comprehensive integration testing with real infrastructure
|
||||
|
||||
**Phase 2 - Workflow Tracking**: ✅ COMPLETE
|
||||
- Database schema implemented with proper indexing
|
||||
- Event correlation system fully functional
|
||||
- Complete audit trail capability verified
|
||||
|
||||
**Phase 3 - Parameter Standardization**: ✅ COMPLETE
|
||||
- Consistent method signatures across all workflow methods
|
||||
- All events properly tagged with workflow_id
|
||||
- Type safety verified across entire codebase
|
||||
|
||||
**Phase 4 - Asynchronous Job Queuing**:
|
||||
**Goal**: Transform synchronous deployment workflows into background jobs for better user experience and system reliability.
|
||||
|
||||
**Current Problem**:
|
||||
- Deployment API calls are synchronous - users wait for entire tenant provisioning (30-60 seconds)
|
||||
- No retry mechanism for failed operations
|
||||
- HTTP timeouts on long-running deployments
|
||||
- Poor user experience during infrastructure provisioning
|
||||
|
||||
**Solution**: Redis-backed job queue with arq for reliable background processing
|
||||
|
||||
#### Architecture Overview
|
||||
```python
|
||||
# API Layer: Return immediately with job tracking
|
||||
@router.post("/{tenant_id}/deploy")
|
||||
async def deploy_tenant(tenant_id: UUID):
|
||||
# Create workflow record in Postgres
|
||||
workflow = await workflow_repo.create_workflow("tenant_deployment", tenant_id)
|
||||
|
||||
# Enqueue job in Redis
|
||||
job = await arq_pool.enqueue_job('deploy_tenant_task', tenant_id, workflow.id)
|
||||
|
||||
# Return job ID immediately
|
||||
return {"job_id": job.job_id, "workflow_id": workflow.id, "status": "queued"}
|
||||
|
||||
# Background Worker: Process via existing unified workflow
|
||||
async def deploy_tenant_task(ctx, tenant_id: str, workflow_id: str):
|
||||
# Existing workflow logic - zero changes needed!
|
||||
await workflow_manager.deploy_tenant(UUID(tenant_id), workflow_id=UUID(workflow_id))
|
||||
```
|
||||
|
||||
#### Implementation Tasks
|
||||
|
||||
**Phase 4.1: Core Job Queue Setup** ✅ COMPLETED
|
||||
- [x] **Add arq dependency** - integrated Redis job queue with existing infrastructure
|
||||
- [x] **Create job definitions** - wrapped existing deployment/undeploy workflows as arq tasks
|
||||
- [x] **Update API endpoints** - updated provisioning endpoints to return job IDs instead of waiting for completion
|
||||
- [x] **JobQueueService implementation** - service layer for job enqueueing and status tracking
|
||||
- [x] **Job status tracking** - integrated with existing workflow table for status updates
|
||||
- [x] **Comprehensive testing** - 18 tests covering positive, negative, and edge cases
|
||||
|
||||
**Phase 4.2: Background Worker Implementation** ✅ COMPLETED
|
||||
- [x] **Job status API** - GET /jobs/{job_id}/status endpoint integrated with JobQueueService
|
||||
- [x] **Background worker process** - arq worker to process queued jobs with proper settings and Redis configuration
|
||||
- [x] **Worker settings and configuration** - WorkerSettings class with proper timeouts, max jobs, and error handling
|
||||
- [x] **Fix API endpoints** - updated job status API to use JobQueueService instead of direct Redis access
|
||||
- [x] **Integration testing** - comprehensive end-to-end testing with real ARQ workers and Fly.io infrastructure
|
||||
- [x] **Worker entry points** - dual-purpose entrypoint.sh script and __main__.py module support for both API and worker processes
|
||||
- [x] **Test fixture updates** - fixed all API and service test fixtures to work with job queue dependencies
|
||||
- [x] **AsyncIO event loop fixes** - resolved event loop issues in integration tests for subprocess worker compatibility
|
||||
- [x] **Complete test coverage** - all 46 tests passing across unit, integration, and API test suites
|
||||
- [x] **Type safety verification** - 0 type checking errors across entire ARQ job queue implementation
|
||||
|
||||
#### Phase 4.2 Implementation Summary ✅ COMPLETE
|
||||
|
||||
**Core ARQ Job Queue System:**
|
||||
- **JobQueueService** - Centralized service for job enqueueing, status tracking, and Redis pool management
|
||||
- **deployment_jobs.py** - ARQ job functions that wrap existing deployment/undeploy workflows
|
||||
- **Worker Settings** - Production-ready ARQ configuration with proper timeouts and error handling
|
||||
- **Dual-Process Architecture** - Single Docker image with entrypoint.sh supporting both API and worker modes
|
||||
|
||||
**Key Files Added:**
|
||||
- `apps/cloud/src/basic_memory_cloud/jobs/` - Complete job queue implementation (7 files)
|
||||
- `apps/cloud/entrypoint.sh` - Dual-purpose Docker container entry point
|
||||
- `apps/cloud/tests/integration/test_worker_integration.py` - Real infrastructure integration tests
|
||||
- `apps/cloud/src/basic_memory_cloud/schemas/job_responses.py` - API response schemas
|
||||
|
||||
**API Integration:**
|
||||
- Provisioning endpoints return job IDs immediately instead of blocking for 60+ seconds
|
||||
- Job status API endpoints for real-time monitoring of deployment progress
|
||||
- Proper error handling and job failure scenarios with detailed error messages
|
||||
|
||||
**Testing Achievement:**
|
||||
- **46 total tests passing** across all test suites (unit, integration, API, services)
|
||||
- **Real infrastructure testing** - ARQ workers process actual Fly.io deployments
|
||||
- **Event loop safety** - Fixed asyncio issues for subprocess worker compatibility
|
||||
- **Test fixture updates** - All fixtures properly support job queue dependencies
|
||||
- **Type checking** - 0 errors across entire codebase
|
||||
|
||||
**Technical Metrics:**
|
||||
- **38 files changed** - +1,736 insertions, -334 deletions
|
||||
- **Integration test runtime** - ~18 seconds with real ARQ workers and Fly.io verification
|
||||
- **Event loop isolation** - Proper async session management for subprocess compatibility
|
||||
- **Redis integration** - Production-ready Redis configuration with connection pooling
|
||||
|
||||
**Phase 4.3: Production Hardening** ✅ COMPLETED
|
||||
- [x] **Configure Upstash Redis** - production Redis setup on Fly.io
|
||||
- [x] **Retry logic for external APIs** - exponential backoff for flaky Tigris IAM operations
|
||||
- [x] **Monitoring and observability** - comprehensive Redis queue monitoring with CLI tools
|
||||
- [x] **Error handling improvements** - graceful handling of expected API errors with appropriate log levels
|
||||
- [x] **CLI tooling enhancements** - bulk update commands for CI/CD automation
|
||||
- [x] **Documentation improvements** - comprehensive monitoring guide with Redis patterns
|
||||
- [x] **Job uniqueness** - ARQ-based duplicate prevention for tenant operations
|
||||
- [ ] **Worker scaling** - multiple arq workers for parallel job processing
|
||||
- [ ] **Job persistence** - ensure jobs survive Redis/worker restarts
|
||||
- [ ] **Error alerting** - notifications for failed deployment jobs
|
||||
|
||||
**Phase 4.4: Advanced Features** (Future)
|
||||
- [ ] **Job scheduling** - deploy tenants at specific times
|
||||
- [ ] **Priority queues** - urgent deployments processed first
|
||||
- [ ] **Batch operations** - bulk tenant deployments
|
||||
- [ ] **Job dependencies** - deployment → configuration → activation chains
|
||||
|
||||
#### Benefits Achieved ✅ REALIZED
|
||||
|
||||
**User Experience Improvements:**
|
||||
- **Immediate API responses** - users get job ID instantly vs waiting 60+ seconds for deployment completion
|
||||
- **Real-time job tracking** - status API provides live updates on deployment progress
|
||||
- **Better error visibility** - detailed error messages and job failure tracking
|
||||
- **CI/CD automation ready** - bulk update commands for automated tenant deployments
|
||||
|
||||
**System Reliability:**
|
||||
- **Redis persistence** - jobs survive Redis/worker restarts with proper queue durability
|
||||
- **Idempotent job processing** - jobs can be safely retried without side effects
|
||||
- **Event loop isolation** - worker processes operate independently from API server
|
||||
- **Retry resilience** - exponential backoff for flaky external API calls (3 attempts, 1s/2s delays)
|
||||
- **Graceful error handling** - expected API errors logged at INFO level, unexpected at ERROR level
|
||||
- **Job uniqueness** - prevent duplicate tenant operations with ARQ's built-in uniqueness feature
|
||||
|
||||
**Operational Benefits:**
|
||||
- **Horizontal scaling ready** - architecture supports adding more workers for parallel processing
|
||||
- **Comprehensive testing** - real infrastructure integration tests ensure production reliability
|
||||
- **Type safety** - full type checking prevents runtime errors in job processing
|
||||
- **Clean separation** - API and worker processes use same codebase with different entry points
|
||||
- **Queue monitoring** - Redis CLI integration for real-time queue activity monitoring
|
||||
- **Comprehensive documentation** - detailed monitoring guide with Redis pattern explanations
|
||||
|
||||
**Development Benefits:**
|
||||
- **Zero workflow changes** - existing deployment/undeploy workflows work unchanged as background jobs
|
||||
- **Async/await native** - modern Python asyncio patterns throughout the implementation
|
||||
- **Event correlation preserved** - all existing workflow tracking and event sourcing continues to work
|
||||
- **Enhanced CLI tooling** - unified tenant commands with proper endpoint routing
|
||||
- **Database integrity** - proper foreign key constraint handling in tenant deletion
|
||||
|
||||
#### Infrastructure Requirements
|
||||
- **Local**: Redis via docker-compose (already exists) ✅
|
||||
- **Production**: Upstash Redis on Fly.io (already configured) ✅
|
||||
- **Workers**: arq worker processes (new deployment target)
|
||||
- **Monitoring**: Job status dashboard (simple web interface)
|
||||
|
||||
#### API Evolution
|
||||
```python
|
||||
# Before: Synchronous (blocks for 60+ seconds)
|
||||
POST /tenant/{id}/deploy → {status: "active", machine_id: "..."}
|
||||
|
||||
# After: Asynchronous (returns immediately)
|
||||
POST /tenant/{id}/deploy → {job_id: "uuid", workflow_id: "uuid", status: "queued"}
|
||||
GET /jobs/{job_id}/status → {status: "running", progress: "deploying_machine", workflow_id: "uuid"}
|
||||
GET /workflows/{workflow_id}/events → [...] # Existing event tracking works unchanged
|
||||
```
|
||||
|
||||
**Technology Choice**: **arq (Redis)** over pgqueuer
|
||||
- **Existing Redis infrastructure** - Upstash + docker-compose already configured
|
||||
- **Better ecosystem** - monitoring tools, documentation, community
|
||||
- **Made by pydantic team** - aligns with existing Python stack
|
||||
- **Hybrid approach** - Redis for queue operations + Postgres for workflow state
|
||||
|
||||
#### Job Uniqueness Implementation
|
||||
|
||||
**Problem**: Multiple concurrent deployment requests for the same tenant could create duplicate jobs, wasting resources and potentially causing conflicts.
|
||||
|
||||
**Solution**: Leverage ARQ's built-in job uniqueness feature using predictable job IDs:
|
||||
|
||||
```python
|
||||
# JobQueueService implementation
|
||||
async def enqueue_deploy_job(self, tenant_id: UUID, image_tag: str | None = None) -> str:
|
||||
unique_job_id = f"deploy-{tenant_id}"
|
||||
|
||||
job = await self.redis_pool.enqueue_job(
|
||||
"deploy_tenant_job",
|
||||
str(tenant_id),
|
||||
image_tag,
|
||||
_job_id=unique_job_id, # ARQ prevents duplicates
|
||||
)
|
||||
|
||||
if job is None:
|
||||
# Job already exists - return existing job ID
|
||||
return unique_job_id
|
||||
else:
|
||||
# New job created - return ARQ job ID
|
||||
return job.job_id
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- **Predictable Job IDs**: `deploy-{tenant_id}`, `undeploy-{tenant_id}`
|
||||
- **Duplicate Prevention**: ARQ returns `None` for duplicate job IDs
|
||||
- **Graceful Handling**: Return existing job ID instead of raising errors
|
||||
- **Idempotent Operations**: Safe to retry deployment requests
|
||||
- **Clear Logging**: Distinguish "Enqueued new" vs "Found existing" jobs
|
||||
|
||||
**Benefits:**
|
||||
- Prevents resource waste from duplicate deployments
|
||||
- Eliminates race conditions from concurrent requests
|
||||
- Makes job monitoring more predictable with consistent IDs
|
||||
- Provides natural deduplication without complex locking mechanisms
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
### Design Philosophy Lessons
|
||||
- **Simplicity beats framework magic** - removing DBOS made the system more reliable and debuggable
|
||||
- **Event sourcing > complex orchestration** - database-backed event tracking provides better observability than framework abstractions
|
||||
- **Idempotent operations > resumable workflows** - each step handling its own retry logic is simpler than framework-managed resumability
|
||||
- **Explicit error handling > framework exception handling** - Python exceptions are clearer than orchestration framework error states
|
||||
|
||||
### Future Considerations
|
||||
- **Monitoring integration** - workflow tracking events could feed into observability systems
|
||||
- **Performance optimization** - event querying patterns may benefit from additional indexing
|
||||
- **Audit compliance** - complete event trail supports regulatory requirements
|
||||
- **Operational dashboards** - workflow status could drive tenant health monitoring
|
||||
|
||||
### Related Specifications
|
||||
- **SPEC-8**: TigrisFS Integration - bucket provisioning integrated with deployment workflow
|
||||
- **SPEC-1**: Specification-Driven Development Process - this spec follows the established format
|
||||
|
||||
## Observations
|
||||
|
||||
- [architecture] Removing framework complexity led to more maintainable system #simplification
|
||||
- [workflow] Single unified deployment workflow handles both provisioning and updates #consolidation
|
||||
- [observability] Event sourcing with workflow correlation provides complete audit trail #event-tracking
|
||||
- [database] Foreign key relationships between workflows and events enable powerful queries #schema-design
|
||||
- [testing] Integration tests with real infrastructure catch edge cases that unit tests miss #testing-strategy
|
||||
- [parameters] Consistent method signatures (workflow_id first) reduce cognitive overhead #api-design
|
||||
- [maintenance] Fewer workflows and dependencies reduce long-term maintenance burden #operational-excellence
|
||||
- [debugging] Plain Python exceptions are clearer than framework abstraction layers #developer-experience
|
||||
- [resilience] Exponential backoff retry patterns handle flaky external API calls gracefully #error-handling
|
||||
- [monitoring] Redis queue monitoring provides real-time operational visibility #observability
|
||||
- [ci-cd] Bulk update commands enable automated tenant deployments in continuous delivery pipelines #automation
|
||||
- [documentation] Comprehensive monitoring guides reduce operational learning curve #knowledge-management
|
||||
- [error-logging] Context-aware log levels (INFO for expected errors, ERROR for unexpected) improve signal-to-noise ratio #logging-strategy
|
||||
- [job-uniqueness] ARQ job uniqueness with predictable tenant-based IDs prevents duplicate operations and resource waste #deduplication
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Configuration Integration
|
||||
- **Redis Configuration**: Add Redis settings to existing `apps/cloud/src/basic_memory_cloud/config.py`
|
||||
- **Local Development**: Leverage existing Redis setup from `docker-compose.yml`
|
||||
- **Production**: Use Upstash Redis configuration for production environments
|
||||
|
||||
### Docker Entrypoint Strategy
|
||||
Create `entrypoint.sh` script to toggle between API server and worker processes using single Docker image:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Entrypoint script for Basic Memory Cloud service
|
||||
# Supports multiple process types: api, worker
|
||||
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
"api")
|
||||
echo "Starting Basic Memory Cloud API server..."
|
||||
exec uvicorn basic_memory_cloud.main:app \
|
||||
--host 0.0.0.0 \
|
||||
--port 8000 \
|
||||
--log-level info
|
||||
;;
|
||||
"worker")
|
||||
echo "Starting Basic Memory Cloud ARQ worker..."
|
||||
# For ARQ worker implementation
|
||||
exec python -m arq basic_memory_cloud.jobs.settings.WorkerSettings
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {api|worker}"
|
||||
echo " api - Start the FastAPI server"
|
||||
echo " worker - Start the ARQ worker"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
### Fly.io Process Groups Configuration
|
||||
Use separate machine groups for API and worker processes with independent scaling:
|
||||
|
||||
```toml
|
||||
# fly.toml app configuration for basic-memory-cloud
|
||||
app = 'basic-memory-cloud-dev-basic-machines'
|
||||
primary_region = 'dfw'
|
||||
org = 'basic-machines'
|
||||
kill_signal = 'SIGINT'
|
||||
kill_timeout = '5s'
|
||||
|
||||
[build]
|
||||
|
||||
# Process groups for API server and worker
|
||||
[processes]
|
||||
api = "api"
|
||||
worker = "worker"
|
||||
|
||||
# Machine scaling configuration
|
||||
[[machine]]
|
||||
size = 'shared-cpu-1x'
|
||||
processes = ['api']
|
||||
min_machines_running = 1
|
||||
auto_stop_machines = false
|
||||
auto_start_machines = true
|
||||
|
||||
[[machine]]
|
||||
size = 'shared-cpu-1x'
|
||||
processes = ['worker']
|
||||
min_machines_running = 1
|
||||
auto_stop_machines = false
|
||||
auto_start_machines = true
|
||||
|
||||
[env]
|
||||
# Python configuration
|
||||
PYTHONUNBUFFERED = '1'
|
||||
PYTHONPATH = '/app'
|
||||
|
||||
# Logging configuration
|
||||
LOG_LEVEL = 'DEBUG'
|
||||
|
||||
# Redis configuration for ARQ
|
||||
REDIS_URL = 'redis://basic-memory-cloud-redis.upstash.io'
|
||||
|
||||
# Database configuration
|
||||
DATABASE_HOST = 'basic-memory-cloud-db-dev-basic-machines.internal'
|
||||
DATABASE_PORT = '5432'
|
||||
DATABASE_NAME = 'basic_memory_cloud'
|
||||
DATABASE_USER = 'postgres'
|
||||
DATABASE_SSL = 'true'
|
||||
|
||||
# Worker configuration
|
||||
ARQ_MAX_JOBS = '10'
|
||||
ARQ_KEEP_RESULT = '3600'
|
||||
|
||||
# Fly.io configuration
|
||||
FLY_ORG = 'basic-machines'
|
||||
FLY_REGION = 'dfw'
|
||||
|
||||
# Internal service - no external HTTP exposure for worker
|
||||
# API accessible via basic-memory-cloud-dev-basic-machines.flycast:8000
|
||||
|
||||
[[vm]]
|
||||
size = 'shared-cpu-1x'
|
||||
```
|
||||
|
||||
### Benefits of This Architecture
|
||||
- **Single Docker Image**: Both API and worker use same container with different entrypoints
|
||||
- **Independent Scaling**: Scale API and worker processes separately based on demand
|
||||
- **Clean Separation**: Web traffic handling separate from background job processing
|
||||
- **Existing Infrastructure**: Leverages current PostgreSQL + Redis setup without complexity
|
||||
- **Hybrid State Management**: Redis for queue operations, PostgreSQL for persistent workflow tracking
|
||||
|
||||
## Relations
|
||||
|
||||
- implements [[SPEC-8 TigrisFS Integration]]
|
||||
- follows [[SPEC-1 Specification-Driven Development Process]]
|
||||
- supersedes previous multi-workflow architecture
|
||||
@@ -0,0 +1,186 @@
|
||||
---
|
||||
title: 'SPEC-11: Basic Memory API Performance Optimization'
|
||||
type: spec
|
||||
permalink: specs/spec-11-basic-memory-api-performance-optimization
|
||||
tags:
|
||||
- performance
|
||||
- api
|
||||
- mcp
|
||||
- database
|
||||
- cloud
|
||||
---
|
||||
|
||||
# SPEC-11: Basic Memory API Performance Optimization
|
||||
|
||||
## Why
|
||||
|
||||
The Basic Memory API experiences significant performance issues in cloud environments due to expensive per-request initialization. MCP tools making
|
||||
HTTP requests to the API suffer from 350ms-2.6s latency overhead **before** any actual operation occurs.
|
||||
|
||||
**Root Cause Analysis:**
|
||||
- GitHub Issue #82 shows repeated initialization sequences in logs (16:29:35 and 16:49:58)
|
||||
- Each MCP tool call triggers full database initialization + project reconciliation
|
||||
- `get_engine_factory()` dependency calls `db.get_or_create_db()` on every request
|
||||
- `reconcile_projects_with_config()` runs expensive sync operations repeatedly
|
||||
|
||||
**Performance Impact:**
|
||||
- Database connection setup: ~50-100ms per request
|
||||
- Migration checks: ~100-500ms per request
|
||||
- Project reconciliation: ~200ms-2s per request
|
||||
- **Total overhead**: ~350ms-2.6s per MCP tool call
|
||||
|
||||
This creates compounding effects with tenant auto-start delays and increases timeout risk in cloud deployments.
|
||||
|
||||
## What
|
||||
|
||||
This optimization affects the **core basic-memory repository** components:
|
||||
|
||||
1. **API Lifespan Management** (`src/basic_memory/api/app.py`)
|
||||
- Cache database connections in app state during startup
|
||||
- Avoid repeated expensive initialization
|
||||
|
||||
2. **Dependency Injection** (`src/basic_memory/deps.py`)
|
||||
- Modify `get_engine_factory()` to use cached connections
|
||||
- Eliminate per-request database setup
|
||||
|
||||
3. **Initialization Service** (`src/basic_memory/services/initialization.py`)
|
||||
- Add caching/throttling to project reconciliation
|
||||
- Skip expensive operations when appropriate
|
||||
|
||||
4. **Configuration** (`src/basic_memory/config.py`)
|
||||
- Add optional performance flags for cloud environments
|
||||
|
||||
**Backwards Compatibility**: All changes must be backwards compatible with existing CLI and non-cloud usage.
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### Phase 1: Cache Database Connections (Critical - 80% of gains)
|
||||
|
||||
**Problem**: `get_engine_factory()` calls `db.get_or_create_db()` per request
|
||||
**Solution**: Cache database engine/session in app state during lifespan
|
||||
|
||||
1. **Modify API Lifespan** (`api/app.py`):
|
||||
```python
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
app_config = ConfigManager().config
|
||||
await initialize_app(app_config)
|
||||
|
||||
# Cache database connection in app state
|
||||
engine, session_maker = await db.get_or_create_db(app_config.database_path)
|
||||
app.state.engine = engine
|
||||
app.state.session_maker = session_maker
|
||||
|
||||
# ... rest of startup logic
|
||||
```
|
||||
|
||||
2. Modify Dependency Injection (deps.py):
|
||||
```python
|
||||
async def get_engine_factory(
|
||||
request: Request
|
||||
) -> tuple[AsyncEngine, async_sessionmaker[AsyncSession]]:
|
||||
"""Get cached engine and session maker from app state."""
|
||||
return request.app.state.engine, request.app.state.session_maker
|
||||
```
|
||||
Phase 2: Optimize Project Reconciliation (Secondary - 20% of gains)
|
||||
|
||||
Problem: reconcile_projects_with_config() runs expensive sync repeatedly
|
||||
Solution: Add module-level caching with time-based throttling
|
||||
|
||||
1. Add Reconciliation Cache (services/initialization.py):
|
||||
```ptyhon
|
||||
_project_reconciliation_completed = False
|
||||
_last_reconciliation_time = 0
|
||||
|
||||
async def reconcile_projects_with_config(app_config, force=False):
|
||||
# Skip if recently completed (within 60 seconds) unless forced
|
||||
if recently_completed and not force:
|
||||
return
|
||||
# ... existing logic
|
||||
```
|
||||
Phase 3: Cloud Environment Flags (Optional)
|
||||
|
||||
Problem: Force expensive initialization in production environments
|
||||
Solution: Add skip flags for cloud/stateless deployments
|
||||
|
||||
1. Add Config Flag (config.py):
|
||||
skip_initialization_sync: bool = Field(default=False)
|
||||
2. Configure in Cloud (basic-memory-cloud integration):
|
||||
BASIC_MEMORY_SKIP_INITIALIZATION_SYNC=true
|
||||
|
||||
How to Evaluate
|
||||
|
||||
Success Criteria
|
||||
|
||||
1. Performance Metrics (Primary):
|
||||
- MCP tool response time reduced by 50%+ (measure before/after)
|
||||
- Database connection overhead eliminated (0ms vs 50-100ms)
|
||||
- Migration check overhead eliminated (0ms vs 100-500ms)
|
||||
- Project reconciliation overhead reduced by 90%+
|
||||
2. Load Testing:
|
||||
- Concurrent MCP tool calls maintain performance
|
||||
- No memory leaks in cached connections
|
||||
- Database connection pool behaves correctly
|
||||
3. Functional Correctness:
|
||||
- All existing API endpoints work identically
|
||||
- MCP tools maintain full functionality
|
||||
- CLI operations unaffected
|
||||
- Database migrations still execute properly
|
||||
4. Backwards Compatibility:
|
||||
- No breaking changes to existing APIs
|
||||
- Config changes are optional with safe defaults
|
||||
- Non-cloud deployments work unchanged
|
||||
|
||||
Testing Strategy
|
||||
|
||||
Performance Testing:
|
||||
# Before optimization
|
||||
time basic-memory-mcp-tools write_note "test" "content" "folder"
|
||||
# Measure: ~1-3 seconds
|
||||
|
||||
# After optimization
|
||||
time basic-memory-mcp-tools write_note "test" "content" "folder"
|
||||
# Target: <500ms
|
||||
|
||||
Load Testing:
|
||||
# Multiple concurrent MCP tool calls
|
||||
for i in {1..10}; do
|
||||
basic-memory-mcp-tools search "test" &
|
||||
done
|
||||
wait
|
||||
# Verify: No degradation, consistent response times
|
||||
|
||||
Regression Testing:
|
||||
# Full basic-memory test suite
|
||||
just test
|
||||
# All tests must pass
|
||||
|
||||
# Integration tests with cloud deployment
|
||||
# Verify MCP gateway → API → database flow works
|
||||
|
||||
Validation Checklist
|
||||
|
||||
- Phase 1 Complete: Database connections cached, dependency injection optimized
|
||||
- Performance Benchmark: 50%+ improvement in MCP tool response times
|
||||
- Memory Usage: No leaks in cached connections over 24h+ periods
|
||||
- Stress Testing: 100+ concurrent requests maintain performance
|
||||
- Backwards Compatibility: All existing functionality preserved
|
||||
- Documentation: Performance optimization documented in README
|
||||
- Cloud Integration: basic-memory-cloud sees performance benefits
|
||||
|
||||
Notes
|
||||
|
||||
Implementation Priority:
|
||||
- Phase 1 provides 80% of performance gains and should be implemented first
|
||||
- Phase 2 provides remaining 20% and addresses edge cases
|
||||
- Phase 3 is optional for maximum cloud optimization
|
||||
|
||||
Risk Mitigation:
|
||||
- All changes backwards compatible
|
||||
- Gradual rollout possible (Phase 1 → 2 → 3)
|
||||
- Easy rollback via configuration flags
|
||||
|
||||
Cloud Integration:
|
||||
- This optimization directly addresses basic-memory-cloud issue #82
|
||||
- Changes in core basic-memory will benefit all cloud tenants
|
||||
- No changes needed in basic-memory-cloud itself
|
||||
@@ -0,0 +1,182 @@
|
||||
# SPEC-12: OpenTelemetry Observability
|
||||
|
||||
## Why
|
||||
|
||||
We need comprehensive observability for basic-memory-cloud to:
|
||||
- Track request flows across our multi-tenant architecture (MCP → Cloud → API services)
|
||||
- Debug performance issues and errors in production
|
||||
- Understand user behavior and system usage patterns
|
||||
- Correlate issues to specific tenants for targeted debugging
|
||||
- Monitor service health and latency across the distributed system
|
||||
|
||||
Currently, we only have basic logging without request correlation or distributed tracing capabilities.
|
||||
|
||||
## What
|
||||
|
||||
Implement OpenTelemetry instrumentation across all basic-memory-cloud services with:
|
||||
|
||||
### Core Requirements
|
||||
1. **Distributed Tracing**: End-to-end request tracing from MCP gateway through to tenant API instances
|
||||
2. **Tenant Correlation**: All traces tagged with tenant_id, user_id, and workos_user_id
|
||||
3. **Service Identification**: Clear service naming and namespace separation
|
||||
4. **Auto-instrumentation**: Automatic tracing for FastAPI, SQLAlchemy, HTTP clients
|
||||
5. **Grafana Cloud Integration**: Direct OTLP export to Grafana Cloud Tempo
|
||||
|
||||
### Services to Instrument
|
||||
- **MCP Gateway** (basic-memory-mcp): Entry point with JWT extraction
|
||||
- **Cloud Service** (basic-memory-cloud): Provisioning and management operations
|
||||
- **API Service** (basic-memory-api): Tenant-specific instances
|
||||
- **Worker Processes** (ARQ workers): Background job processing
|
||||
|
||||
### Key Trace Attributes
|
||||
- `tenant.id`: UUID from UserProfile.tenant_id
|
||||
- `user.id`: WorkOS user identifier
|
||||
- `user.email`: User email for debugging
|
||||
- `service.name`: Specific service identifier
|
||||
- `service.namespace`: Environment (development/production)
|
||||
- `operation.type`: Business operation (provision/update/delete)
|
||||
- `tenant.app_name`: Fly.io app name for tenant instances
|
||||
|
||||
## How
|
||||
|
||||
### Phase 1: Setup OpenTelemetry SDK
|
||||
1. Add OpenTelemetry dependencies to each service's pyproject.toml:
|
||||
```python
|
||||
"opentelemetry-distro[otlp]>=1.29.0",
|
||||
"opentelemetry-instrumentation-fastapi>=0.50b0",
|
||||
"opentelemetry-instrumentation-httpx>=0.50b0",
|
||||
"opentelemetry-instrumentation-sqlalchemy>=0.50b0",
|
||||
"opentelemetry-instrumentation-logging>=0.50b0",
|
||||
```
|
||||
|
||||
2. Create shared telemetry initialization module (`apps/shared/telemetry.py`)
|
||||
|
||||
3. Configure Grafana Cloud OTLP endpoint via environment variables:
|
||||
```bash
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-east-2.grafana.net/otlp
|
||||
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic[token]
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
||||
```
|
||||
|
||||
### Phase 2: Instrument MCP Gateway
|
||||
1. Extract tenant context from AuthKit JWT in middleware
|
||||
2. Create root span with tenant attributes
|
||||
3. Propagate trace context to downstream services via headers
|
||||
|
||||
### Phase 3: Instrument Cloud Service
|
||||
1. Continue trace from MCP gateway
|
||||
2. Add operation-specific attributes (provisioning events)
|
||||
3. Instrument ARQ worker jobs for async operations
|
||||
4. Track Fly.io API calls and latency
|
||||
|
||||
### Phase 4: Instrument API Service
|
||||
1. Extract tenant context from JWT
|
||||
2. Add machine-specific metadata (instance ID, region)
|
||||
3. Instrument database operations with SQLAlchemy
|
||||
4. Track MCP protocol operations
|
||||
|
||||
### Phase 5: Configure and Deploy
|
||||
1. Add OTLP configuration to `.env.example` and `.env.example.secrets`
|
||||
2. Set Fly.io secrets for production deployment
|
||||
3. Update Dockerfiles to use `opentelemetry-instrument` wrapper
|
||||
4. Deploy to development environment first for testing
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
1. **End-to-end traces visible in Grafana Cloud** showing complete request flow
|
||||
2. **Tenant filtering works** - Can filter traces by tenant_id to see all requests for a user
|
||||
3. **Service maps accurate** - Grafana shows correct service dependencies
|
||||
4. **Performance overhead < 5%** - Minimal latency impact from instrumentation
|
||||
5. **Error correlation** - Can trace errors back to specific tenant and operation
|
||||
|
||||
### Testing Checklist
|
||||
- [x] Single request creates connected trace across all services
|
||||
- [x] Tenant attributes present on all spans
|
||||
- [x] Background jobs (ARQ) appear in traces
|
||||
- [x] Database queries show in trace timeline
|
||||
- [x] HTTP calls to Fly.io API tracked
|
||||
- [x] Traces exported successfully to Grafana Cloud
|
||||
- [x] Can search traces by tenant_id in Grafana
|
||||
- [x] Service dependency graph shows correct flow
|
||||
|
||||
### Monitoring Success
|
||||
- All services reporting traces to Grafana Cloud
|
||||
- No OTLP export errors in logs
|
||||
- Trace sampling working correctly (if implemented)
|
||||
- Resource usage acceptable (CPU/memory)
|
||||
|
||||
## Dependencies
|
||||
- Grafana Cloud account with OTLP endpoint configured
|
||||
- OpenTelemetry Python SDK v1.29.0+
|
||||
- FastAPI instrumentation compatibility
|
||||
- Network access from Fly.io to Grafana Cloud
|
||||
|
||||
## Implementation Assignment
|
||||
**Recommended Agent**: python-developer
|
||||
- Requires Python/FastAPI expertise
|
||||
- Needs understanding of distributed systems
|
||||
- Must implement middleware and context propagation
|
||||
- Should understand OpenTelemetry SDK and instrumentation
|
||||
|
||||
## Follow-up Tasks
|
||||
|
||||
### Enhanced Log Correlation
|
||||
While basic trace-to-log correlation works automatically via OpenTelemetry logging instrumentation, consider adding structured logging for improved log filtering:
|
||||
|
||||
1. **Structured Logging Context**: Add `logger.bind()` calls to inject tenant/user context directly into log records
|
||||
2. **Custom Loguru Formatter**: Extract OpenTelemetry span attributes for better log readability
|
||||
3. **Direct Log Filtering**: Enable searching logs directly by tenant_id, workflow_id without going through traces
|
||||
|
||||
This would complement the existing automatic trace correlation and provide better log search capabilities.
|
||||
|
||||
## Alternative Solution: Logfire
|
||||
|
||||
After implementing OpenTelemetry with Grafana Cloud, we discovered limitations in the observability experience:
|
||||
- Traces work but lack useful context without correlated logs
|
||||
- Setting up log correlation with Grafana is complex and requires additional infrastructure
|
||||
- The developer experience for Python observability is suboptimal
|
||||
|
||||
### Logfire Evaluation
|
||||
|
||||
**Pydantic Logfire** offers a compelling alternative that addresses your specific requirements:
|
||||
|
||||
#### Core Requirements Match
|
||||
- ✅ **User Activity Tracking**: Automatic request tracing with business context
|
||||
- ✅ **Error Monitoring**: Built-in exception tracking with full context
|
||||
- ✅ **Performance Metrics**: Automatic latency and performance monitoring
|
||||
- ✅ **Request Tracing**: Native distributed tracing across services
|
||||
- ✅ **Log Correlation**: Seamless trace-to-log correlation without setup
|
||||
|
||||
#### Key Advantages
|
||||
1. **Python-First Design**: Built specifically for Python/FastAPI applications by the Pydantic team
|
||||
2. **Simple Integration**: `pip install logfire` + `logfire.configure()` vs complex OTLP setup
|
||||
3. **Automatic Correlation**: Logs automatically include trace context without manual configuration
|
||||
4. **Real-time SQL Interface**: Query spans and logs using SQL with auto-completion
|
||||
5. **Better Developer UX**: Purpose-built observability UI vs generic Grafana dashboards
|
||||
6. **Loguru Integration**: `logger.configure(handlers=[logfire.loguru_handler()])` maintains existing logging
|
||||
|
||||
#### Pricing Assessment
|
||||
- **Free Tier**: 10M spans/month (suitable for development and small production workloads)
|
||||
- **Transparent Pricing**: $1 per million spans/metrics after free tier
|
||||
- **No Hidden Costs**: No per-host fees, only usage-based metering
|
||||
- **Production Ready**: Recently exited beta, enterprise features available
|
||||
|
||||
#### Migration Path
|
||||
The existing OpenTelemetry instrumentation is compatible - Logfire uses OpenTelemetry under the hood, so the current spans and attributes would work unchanged.
|
||||
|
||||
### Recommendation
|
||||
|
||||
**Consider migrating to Logfire** for the following reasons:
|
||||
1. It directly addresses the "next to useless" traces problem by providing integrated logs
|
||||
2. Dramatically simpler setup and maintenance compared to Grafana Cloud + custom log correlation
|
||||
3. Better ROI on observability investment with purpose-built Python tooling
|
||||
4. Free tier sufficient for current development needs with clear scaling path
|
||||
|
||||
The current Grafana Cloud implementation provides a solid foundation and could remain as a backup/export target, while Logfire becomes the primary observability platform.
|
||||
|
||||
## Status
|
||||
**Created**: 2024-01-28
|
||||
**Status**: Completed (OpenTelemetry + Grafana Cloud)
|
||||
**Next Phase**: Evaluate Logfire migration
|
||||
**Priority**: High - Critical for production observability
|
||||
@@ -0,0 +1,917 @@
|
||||
---
|
||||
title: 'SPEC-13: CLI Authentication with Subscription Validation'
|
||||
type: spec
|
||||
permalink: specs/spec-12-cli-auth-subscription-validation
|
||||
tags:
|
||||
- authentication
|
||||
- security
|
||||
- cli
|
||||
- subscription
|
||||
status: draft
|
||||
created: 2025-10-02
|
||||
---
|
||||
|
||||
# SPEC-13: CLI Authentication with Subscription Validation
|
||||
|
||||
## Why
|
||||
|
||||
The Basic Memory Cloud CLI currently has a security gap in authentication that allows unauthorized access:
|
||||
|
||||
**Current Web Flow (Secure)**:
|
||||
1. User signs up via WorkOS AuthKit
|
||||
2. User creates Polar subscription
|
||||
3. Web app validates subscription before calling `POST /tenants/setup`
|
||||
4. Tenant provisioned only after subscription validation ✅
|
||||
|
||||
**Current CLI Flow (Insecure)**:
|
||||
1. User signs up via WorkOS AuthKit (OAuth device flow)
|
||||
2. User runs `bm cloud login`
|
||||
3. CLI receives JWT token from WorkOS
|
||||
4. CLI can access all cloud endpoints without subscription check ❌
|
||||
|
||||
**Problem**: Anyone can sign up with WorkOS and immediately access cloud infrastructure via CLI without having an active Polar subscription. This creates:
|
||||
- Revenue loss (free resource consumption)
|
||||
- Security risk (unauthorized data access)
|
||||
- Support burden (users accessing features they haven't paid for)
|
||||
|
||||
**Root Cause**: The CLI authentication flow validates JWT tokens but doesn't verify subscription status before granting access to cloud resources.
|
||||
|
||||
## What
|
||||
|
||||
Add subscription validation to authentication flow to ensure only users with active Polar subscriptions can access cloud resources across all access methods (CLI, MCP, Web App, Direct API).
|
||||
|
||||
**Affected Components**:
|
||||
|
||||
### basic-memory-cloud (Cloud Service)
|
||||
- `apps/cloud/src/basic_memory_cloud/deps.py` - Add subscription validation dependency
|
||||
- `apps/cloud/src/basic_memory_cloud/services/subscription_service.py` - Add subscription check method
|
||||
- `apps/cloud/src/basic_memory_cloud/api/tenant_mount.py` - Protect mount endpoints
|
||||
- `apps/cloud/src/basic_memory_cloud/api/proxy.py` - Protect proxy endpoints
|
||||
|
||||
### basic-memory (CLI)
|
||||
- `src/basic_memory/cli/commands/cloud/core_commands.py` - Handle 403 errors
|
||||
- `src/basic_memory/cli/commands/cloud/api_client.py` - Parse subscription errors
|
||||
- `docs/cloud-cli.md` - Document subscription requirement
|
||||
|
||||
**Endpoints to Protect**:
|
||||
- `GET /tenant/mount/info` - Used by CLI bisync setup
|
||||
- `POST /tenant/mount/credentials` - Used by CLI bisync credentials
|
||||
- `GET /proxy/{path:path}` - Used by Web App, MCP tools, CLI tools, Direct API
|
||||
- All other `/proxy/*` endpoints - Centralized access point for all user operations
|
||||
|
||||
## Complete Authentication Flow Analysis
|
||||
|
||||
### Overview of All Access Flows
|
||||
|
||||
Basic Memory Cloud has **7 distinct authentication flows**. This spec closes subscription validation gaps in flows 2-4 and 6, which all converge on the `/proxy/*` endpoints.
|
||||
|
||||
### Flow 1: Polar Webhook → Registration ✅ SECURE
|
||||
```
|
||||
Polar webhook → POST /api/webhooks/polar
|
||||
→ Validates Polar webhook signature
|
||||
→ Creates/updates subscription in database
|
||||
→ No direct user access - webhook only
|
||||
```
|
||||
**Auth**: Polar webhook signature validation
|
||||
**Subscription Check**: N/A (webhook creates subscriptions)
|
||||
**Status**: ✅ Secure - webhook validated, no user JWT involved
|
||||
|
||||
### Flow 2: Web App Login ❌ NEEDS FIX
|
||||
```
|
||||
User → apps/web (Vue.js/Nuxt)
|
||||
→ WorkOS AuthKit magic link authentication
|
||||
→ JWT stored in browser session
|
||||
→ Web app calls /proxy/{project}/... endpoints (memory, directory, projects)
|
||||
→ proxy.py validates JWT but does NOT check subscription
|
||||
→ Access granted without subscription ❌
|
||||
```
|
||||
**Auth**: WorkOS JWT via `CurrentUserProfileHybridJwtDep`
|
||||
**Subscription Check**: ❌ Missing
|
||||
**Fixed By**: Task 1.4 (protect `/proxy/*` endpoints)
|
||||
|
||||
### Flow 3: MCP (Model Context Protocol) ❌ NEEDS FIX
|
||||
```
|
||||
AI Agent (Claude, Cursor, etc.) → https://mcp.basicmemory.com
|
||||
→ AuthKit OAuth device flow
|
||||
→ JWT stored in AI agent
|
||||
→ MCP tools call {cloud_host}/proxy/{endpoint} with Authorization header
|
||||
→ proxy.py validates JWT but does NOT check subscription
|
||||
→ MCP tools can access all cloud resources without subscription ❌
|
||||
```
|
||||
**Auth**: AuthKit JWT via `CurrentUserProfileHybridJwtDep`
|
||||
**Subscription Check**: ❌ Missing
|
||||
**Fixed By**: Task 1.4 (protect `/proxy/*` endpoints)
|
||||
|
||||
### Flow 4: CLI Auth (basic-memory) ❌ NEEDS FIX
|
||||
```
|
||||
User → bm cloud login
|
||||
→ AuthKit OAuth device flow
|
||||
→ JWT stored in ~/.basic-memory/tokens.json
|
||||
→ CLI calls:
|
||||
- {cloud_host}/tenant/mount/info (for bisync setup)
|
||||
- {cloud_host}/tenant/mount/credentials (for bisync credentials)
|
||||
- {cloud_host}/proxy/{endpoint} (for all MCP tools)
|
||||
→ tenant_mount.py and proxy.py validate JWT but do NOT check subscription
|
||||
→ Access granted without subscription ❌
|
||||
```
|
||||
**Auth**: AuthKit JWT via `CurrentUserProfileHybridJwtDep`
|
||||
**Subscription Check**: ❌ Missing
|
||||
**Fixed By**: Task 1.3 (protect `/tenant/mount/*`) + Task 1.4 (protect `/proxy/*`)
|
||||
|
||||
### Flow 5: Cloud CLI (Admin Tasks) ✅ SECURE
|
||||
```
|
||||
Admin → python -m basic_memory_cloud.cli.tenant_cli
|
||||
→ Uses CLIAuth with admin WorkOS OAuth client
|
||||
→ Gets JWT token with admin org membership
|
||||
→ Calls /tenants/* endpoints (create, list, delete tenants)
|
||||
→ tenants.py validates JWT AND admin org membership via AdminUserHybridDep
|
||||
→ Access granted only to admin organization members ✅
|
||||
```
|
||||
**Auth**: AuthKit JWT + Admin org validation via `AdminUserHybridDep`
|
||||
**Subscription Check**: N/A (admins bypass subscription requirement)
|
||||
**Status**: ✅ Secure - admin-only endpoints, separate from user flows
|
||||
|
||||
### Flow 6: Direct API Calls ❌ NEEDS FIX
|
||||
```
|
||||
Any HTTP client → {cloud_host}/proxy/{endpoint}
|
||||
→ Sends Authorization: Bearer {jwt} header
|
||||
→ proxy.py validates JWT but does NOT check subscription
|
||||
→ Direct API access without subscription ❌
|
||||
```
|
||||
**Auth**: WorkOS or AuthKit JWT via `CurrentUserProfileHybridJwtDep`
|
||||
**Subscription Check**: ❌ Missing
|
||||
**Fixed By**: Task 1.4 (protect `/proxy/*` endpoints)
|
||||
|
||||
### Flow 7: Tenant API Instance (Internal) ✅ SECURE
|
||||
```
|
||||
/proxy/* → Tenant API (basic-memory-{tenant_id}.fly.dev)
|
||||
→ Validates signed header from proxy (tenant_id + signature)
|
||||
→ Direct external access will be disabled in production
|
||||
→ Only accessible via /proxy endpoints
|
||||
```
|
||||
**Auth**: Signed header validation from proxy
|
||||
**Subscription Check**: N/A (internal only, validated at proxy layer)
|
||||
**Status**: ✅ Secure - validates proxy signature, not directly accessible
|
||||
|
||||
### Authentication Flow Summary Matrix
|
||||
|
||||
| Flow | Access Method | Current Auth | Subscription Check | Fixed By SPEC-13 |
|
||||
|------|---------------|--------------|-------------------|------------------|
|
||||
| 1. Polar Webhook | Polar webhook → `/api/webhooks/polar` | Polar signature | N/A (webhook) | N/A |
|
||||
| 2. Web App | Browser → `/proxy/*` | WorkOS JWT ✅ | ❌ Missing | ✅ Task 1.4 |
|
||||
| 3. MCP | AI Agent → `/proxy/*` | AuthKit JWT ✅ | ❌ Missing | ✅ Task 1.4 |
|
||||
| 4. CLI | `bm cloud` → `/tenant/mount/*` + `/proxy/*` | AuthKit JWT ✅ | ❌ Missing | ✅ Task 1.3 + 1.4 |
|
||||
| 5. Cloud CLI (Admin) | `tenant_cli` → `/tenants/*` | AuthKit JWT ✅ + Admin org | N/A (admin) | N/A (admin bypass) |
|
||||
| 6. Direct API | HTTP client → `/proxy/*` | WorkOS/AuthKit JWT ✅ | ❌ Missing | ✅ Task 1.4 |
|
||||
| 7. Tenant API | Proxy → tenant instance | Proxy signature ✅ | N/A (internal) | N/A |
|
||||
|
||||
### Key Insights
|
||||
|
||||
1. **Single Point of Failure**: All user access (Web, MCP, CLI, Direct API) converges on `/proxy/*` endpoints
|
||||
2. **Centralized Fix**: Protecting `/proxy/*` with subscription validation closes gaps in flows 2, 3, 4, and 6 simultaneously
|
||||
3. **Admin Bypass**: Cloud CLI admin tasks use separate `/tenants/*` endpoints with admin-only access (no subscription needed)
|
||||
4. **Defense in Depth**: `/tenant/mount/*` endpoints also protected for CLI bisync operations
|
||||
|
||||
### Architecture Benefits
|
||||
|
||||
The `/proxy` layer serves as the **single centralized authorization point** for all user access:
|
||||
- ✅ One place to validate JWT tokens
|
||||
- ✅ One place to check subscription status
|
||||
- ✅ One place to handle tenant routing
|
||||
- ✅ Protects Web App, MCP, CLI, and Direct API simultaneously
|
||||
|
||||
This architecture makes the fix comprehensive and maintainable.
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### Option A: Database Subscription Check (Recommended)
|
||||
|
||||
**Approach**: Add FastAPI dependency that validates subscription status from database before allowing access.
|
||||
|
||||
**Implementation**:
|
||||
|
||||
1. **Create Subscription Validation Dependency** (`deps.py`)
|
||||
```python
|
||||
async def get_authorized_cli_user_profile(
|
||||
credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)],
|
||||
session: DatabaseSessionDep,
|
||||
user_profile_repo: UserProfileRepositoryDep,
|
||||
subscription_service: SubscriptionServiceDep,
|
||||
) -> UserProfile:
|
||||
"""
|
||||
Hybrid authentication with subscription validation for CLI access.
|
||||
|
||||
Validates JWT (WorkOS or AuthKit) and checks for active subscription.
|
||||
Returns UserProfile if both checks pass.
|
||||
"""
|
||||
# Try WorkOS JWT first (faster validation path)
|
||||
try:
|
||||
user_context = await validate_workos_jwt(credentials.credentials)
|
||||
except HTTPException:
|
||||
# Fall back to AuthKit JWT validation
|
||||
try:
|
||||
user_context = await validate_authkit_jwt(credentials.credentials)
|
||||
except HTTPException as e:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Invalid JWT token. Authentication required.",
|
||||
) from e
|
||||
|
||||
# Check subscription status
|
||||
has_subscription = await subscription_service.check_user_has_active_subscription(
|
||||
session, user_context.workos_user_id
|
||||
)
|
||||
|
||||
if not has_subscription:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail={
|
||||
"error": "subscription_required",
|
||||
"message": "Active subscription required for CLI access",
|
||||
"subscribe_url": "https://basicmemory.com/subscribe"
|
||||
}
|
||||
)
|
||||
|
||||
# Look up and return user profile
|
||||
user_profile = await user_profile_repo.get_user_profile_by_workos_user_id(
|
||||
session, user_context.workos_user_id
|
||||
)
|
||||
if not user_profile:
|
||||
raise HTTPException(401, detail="User profile not found")
|
||||
|
||||
return user_profile
|
||||
```
|
||||
|
||||
```python
|
||||
AuthorizedCLIUserProfileDep = Annotated[UserProfile, Depends(get_authorized_cli_user_profile)]
|
||||
```
|
||||
|
||||
2. **Add Subscription Check Method** (`subscription_service.py`)
|
||||
```python
|
||||
async def check_user_has_active_subscription(
|
||||
self, session: AsyncSession, workos_user_id: str
|
||||
) -> bool:
|
||||
"""Check if user has active subscription."""
|
||||
# Use existing repository method to get subscription by workos_user_id
|
||||
# This joins UserProfile -> Subscription in a single query
|
||||
subscription = await self.subscription_repository.get_subscription_by_workos_user_id(
|
||||
session, workos_user_id
|
||||
)
|
||||
|
||||
return subscription is not None and subscription.status == "active"
|
||||
```
|
||||
|
||||
3. **Protect Endpoints** (Replace `CurrentUserProfileHybridJwtDep` with `AuthorizedCLIUserProfileDep`)
|
||||
```python
|
||||
# Before
|
||||
@router.get("/mount/info")
|
||||
async def get_mount_info(
|
||||
user_profile: CurrentUserProfileHybridJwtDep,
|
||||
session: DatabaseSessionDep,
|
||||
):
|
||||
tenant_id = user_profile.tenant_id
|
||||
...
|
||||
|
||||
# After
|
||||
@router.get("/mount/info")
|
||||
async def get_mount_info(
|
||||
user_profile: AuthorizedCLIUserProfileDep, # Now includes subscription check
|
||||
session: DatabaseSessionDep,
|
||||
):
|
||||
tenant_id = user_profile.tenant_id # No changes needed to endpoint logic
|
||||
...
|
||||
```
|
||||
|
||||
4. **Update CLI Error Handling**
|
||||
```python
|
||||
# In core_commands.py login()
|
||||
try:
|
||||
success = await auth.login()
|
||||
if success:
|
||||
# Test subscription by calling protected endpoint
|
||||
await make_api_request("GET", f"{host_url}/tenant/mount/info")
|
||||
except CloudAPIError as e:
|
||||
if e.status_code == 403 and e.detail.get("error") == "subscription_required":
|
||||
console.print("[red]Subscription required[/red]")
|
||||
console.print(f"Subscribe at: {e.detail['subscribe_url']}")
|
||||
raise typer.Exit(1)
|
||||
```
|
||||
|
||||
**Pros**:
|
||||
- Simple to implement
|
||||
- Fast (single database query)
|
||||
- Clear error messages
|
||||
- Works with existing subscription flow
|
||||
|
||||
**Cons**:
|
||||
- Database is source of truth (could get out of sync with Polar)
|
||||
- Adds one extra subscription lookup query per request (lightweight JOIN query)
|
||||
|
||||
### Option B: WorkOS Organizations
|
||||
|
||||
**Approach**: Add users to "beta-users" organization in WorkOS after subscription creation, validate org membership via JWT claims.
|
||||
|
||||
**Implementation**:
|
||||
1. After Polar subscription webhook, add user to WorkOS org via API
|
||||
2. Validate `org_id` claim in JWT matches authorized org
|
||||
3. Use existing `get_admin_workos_jwt` pattern
|
||||
|
||||
**Pros**:
|
||||
- WorkOS as single source of truth
|
||||
- No database queries needed
|
||||
- More secure (harder to bypass)
|
||||
|
||||
**Cons**:
|
||||
- More complex (requires WorkOS API integration)
|
||||
- Requires managing WorkOS org membership
|
||||
- Less control over error messages
|
||||
- Additional API calls during registration
|
||||
|
||||
### Recommendation
|
||||
|
||||
**Start with Option A (Database Check)** for:
|
||||
- Faster implementation
|
||||
- Clearer error messages
|
||||
- Easier testing
|
||||
- Existing subscription infrastructure
|
||||
|
||||
**Consider Option B later** if:
|
||||
- Need tighter security
|
||||
- Want to reduce database dependency
|
||||
- Scale requires fewer database queries
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
|
||||
**1. Unauthorized Users Blocked**
|
||||
- [ ] User without subscription cannot complete `bm cloud login`
|
||||
- [ ] User without subscription receives clear error with subscribe link
|
||||
- [ ] User without subscription cannot run `bm cloud setup`
|
||||
- [ ] User without subscription cannot run `bm sync` in cloud mode
|
||||
|
||||
**2. Authorized Users Work**
|
||||
- [ ] User with active subscription can login successfully
|
||||
- [ ] User with active subscription can setup bisync
|
||||
- [ ] User with active subscription can sync files
|
||||
- [ ] User with active subscription can use all MCP tools via proxy
|
||||
|
||||
**3. Subscription State Changes**
|
||||
- [ ] Expired subscription blocks access with clear error
|
||||
- [ ] Renewed subscription immediately restores access
|
||||
- [ ] Cancelled subscription blocks access after grace period
|
||||
|
||||
**4. Error Messages**
|
||||
- [ ] 403 errors include "subscription_required" error code
|
||||
- [ ] Error messages include subscribe URL
|
||||
- [ ] CLI displays user-friendly messages
|
||||
- [ ] Errors logged appropriately for debugging
|
||||
|
||||
**5. No Regressions**
|
||||
- [ ] Web app login/subscription flow unaffected
|
||||
- [ ] Admin endpoints still work (bypass check)
|
||||
- [ ] Tenant provisioning workflow unchanged
|
||||
- [ ] Performance not degraded
|
||||
|
||||
### Test Cases
|
||||
|
||||
**Manual Testing**:
|
||||
```bash
|
||||
# Test 1: Unauthorized user
|
||||
1. Create new WorkOS account (no subscription)
|
||||
2. Run `bm cloud login`
|
||||
3. Verify: Login succeeds but shows subscription required error
|
||||
4. Verify: Cannot run `bm cloud setup`
|
||||
5. Verify: Clear error message with subscribe link
|
||||
|
||||
# Test 2: Authorized user
|
||||
1. Use account with active Polar subscription
|
||||
2. Run `bm cloud login`
|
||||
3. Verify: Login succeeds without errors
|
||||
4. Run `bm cloud setup`
|
||||
5. Verify: Setup completes successfully
|
||||
6. Run `bm sync`
|
||||
7. Verify: Sync works normally
|
||||
|
||||
# Test 3: Subscription expiration
|
||||
1. Use account with active subscription
|
||||
2. Manually expire subscription in database
|
||||
3. Run `bm cloud login`
|
||||
4. Verify: Blocked with clear error
|
||||
5. Renew subscription
|
||||
6. Run `bm cloud login` again
|
||||
7. Verify: Access restored
|
||||
```
|
||||
|
||||
**Automated Tests**:
|
||||
```python
|
||||
# Test subscription validation dependency
|
||||
async def test_authorized_user_allowed(
|
||||
db_session,
|
||||
user_profile_repo,
|
||||
subscription_service,
|
||||
mock_jwt_credentials
|
||||
):
|
||||
# Create user with active subscription
|
||||
user_profile = await create_user_with_subscription(db_session, status="active")
|
||||
|
||||
# Mock JWT credentials for the user
|
||||
credentials = mock_jwt_credentials(user_profile.workos_user_id)
|
||||
|
||||
# Should not raise exception
|
||||
result = await get_authorized_cli_user_profile(
|
||||
credentials, db_session, user_profile_repo, subscription_service
|
||||
)
|
||||
assert result.id == user_profile.id
|
||||
assert result.workos_user_id == user_profile.workos_user_id
|
||||
|
||||
async def test_unauthorized_user_blocked(
|
||||
db_session,
|
||||
user_profile_repo,
|
||||
subscription_service,
|
||||
mock_jwt_credentials
|
||||
):
|
||||
# Create user without subscription
|
||||
user_profile = await create_user_without_subscription(db_session)
|
||||
credentials = mock_jwt_credentials(user_profile.workos_user_id)
|
||||
|
||||
# Should raise 403
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
await get_authorized_cli_user_profile(
|
||||
credentials, db_session, user_profile_repo, subscription_service
|
||||
)
|
||||
|
||||
assert exc.value.status_code == 403
|
||||
assert exc.value.detail["error"] == "subscription_required"
|
||||
|
||||
async def test_inactive_subscription_blocked(
|
||||
db_session,
|
||||
user_profile_repo,
|
||||
subscription_service,
|
||||
mock_jwt_credentials
|
||||
):
|
||||
# Create user with cancelled/inactive subscription
|
||||
user_profile = await create_user_with_subscription(db_session, status="cancelled")
|
||||
credentials = mock_jwt_credentials(user_profile.workos_user_id)
|
||||
|
||||
# Should raise 403
|
||||
with pytest.raises(HTTPException) as exc:
|
||||
await get_authorized_cli_user_profile(
|
||||
credentials, db_session, user_profile_repo, subscription_service
|
||||
)
|
||||
|
||||
assert exc.value.status_code == 403
|
||||
assert exc.value.detail["error"] == "subscription_required"
|
||||
```
|
||||
|
||||
## Implementation Tasks
|
||||
|
||||
### Phase 1: Cloud Service (basic-memory-cloud)
|
||||
|
||||
#### Task 1.1: Add subscription check method to SubscriptionService ✅
|
||||
**File**: `apps/cloud/src/basic_memory_cloud/services/subscription_service.py`
|
||||
|
||||
- [x] Add method `check_subscription(session: AsyncSession, workos_user_id: str) -> bool`
|
||||
- [x] Use existing `self.subscription_repository.get_subscription_by_workos_user_id(session, workos_user_id)`
|
||||
- [x] Check both `status == "active"` AND `current_period_end >= now()`
|
||||
- [x] Log both values when check fails
|
||||
- [x] Add docstring explaining the method
|
||||
- [x] Run `just typecheck` to verify types
|
||||
|
||||
**Actual implementation**:
|
||||
```python
|
||||
async def check_subscription(
|
||||
self, session: AsyncSession, workos_user_id: str
|
||||
) -> bool:
|
||||
"""Check if user has active subscription with valid period."""
|
||||
subscription = await self.subscription_repository.get_subscription_by_workos_user_id(
|
||||
session, workos_user_id
|
||||
)
|
||||
|
||||
if subscription is None:
|
||||
return False
|
||||
|
||||
if subscription.status != "active":
|
||||
logger.warning("Subscription inactive", workos_user_id=workos_user_id,
|
||||
status=subscription.status, current_period_end=subscription.current_period_end)
|
||||
return False
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
if subscription.current_period_end is None or subscription.current_period_end < now:
|
||||
logger.warning("Subscription expired", workos_user_id=workos_user_id,
|
||||
status=subscription.status, current_period_end=subscription.current_period_end)
|
||||
return False
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
#### Task 1.2: Add subscription validation dependency ✅
|
||||
**File**: `apps/cloud/src/basic_memory_cloud/deps.py`
|
||||
|
||||
- [x] Import necessary types at top of file (if not already present)
|
||||
- [x] Add `authorized_user_profile()` async function
|
||||
- [x] Implement hybrid JWT validation (WorkOS first, AuthKit fallback)
|
||||
- [x] Add subscription check using `subscription_service.check_subscription()`
|
||||
- [x] Raise `HTTPException(403)` with structured error detail if no active subscription
|
||||
- [x] Look up and return `UserProfile` after validation
|
||||
- [x] Add `AuthorizedUserProfileDep` type annotation
|
||||
- [x] Use `settings.subscription_url` from config (env var)
|
||||
- [x] Run `just typecheck` to verify types
|
||||
|
||||
**Expected code**:
|
||||
```python
|
||||
async def get_authorized_cli_user_profile(
|
||||
credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)],
|
||||
session: DatabaseSessionDep,
|
||||
user_profile_repo: UserProfileRepositoryDep,
|
||||
subscription_service: SubscriptionServiceDep,
|
||||
) -> UserProfile:
|
||||
"""
|
||||
Hybrid authentication with subscription validation for CLI access.
|
||||
|
||||
Validates JWT (WorkOS or AuthKit) and checks for active subscription.
|
||||
Returns UserProfile if both checks pass.
|
||||
|
||||
Raises:
|
||||
HTTPException(401): Invalid JWT token
|
||||
HTTPException(403): No active subscription
|
||||
"""
|
||||
# Try WorkOS JWT first (faster validation path)
|
||||
try:
|
||||
user_context = await validate_workos_jwt(credentials.credentials)
|
||||
except HTTPException:
|
||||
# Fall back to AuthKit JWT validation
|
||||
try:
|
||||
user_context = await validate_authkit_jwt(credentials.credentials)
|
||||
except HTTPException as e:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Invalid JWT token. Authentication required.",
|
||||
) from e
|
||||
|
||||
# Check subscription status
|
||||
has_subscription = await subscription_service.check_user_has_active_subscription(
|
||||
session, user_context.workos_user_id
|
||||
)
|
||||
|
||||
if not has_subscription:
|
||||
logger.warning(
|
||||
"CLI access denied: no active subscription",
|
||||
workos_user_id=user_context.workos_user_id,
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail={
|
||||
"error": "subscription_required",
|
||||
"message": "Active subscription required for CLI access",
|
||||
"subscribe_url": "https://basicmemory.com/subscribe"
|
||||
}
|
||||
)
|
||||
|
||||
# Look up and return user profile
|
||||
user_profile = await user_profile_repo.get_user_profile_by_workos_user_id(
|
||||
session, user_context.workos_user_id
|
||||
)
|
||||
if not user_profile:
|
||||
logger.error(
|
||||
"User profile not found after successful auth",
|
||||
workos_user_id=user_context.workos_user_id,
|
||||
)
|
||||
raise HTTPException(401, detail="User profile not found")
|
||||
|
||||
logger.info(
|
||||
"CLI access granted",
|
||||
workos_user_id=user_context.workos_user_id,
|
||||
user_profile_id=str(user_profile.id),
|
||||
)
|
||||
return user_profile
|
||||
|
||||
|
||||
AuthorizedCLIUserProfileDep = Annotated[UserProfile, Depends(get_authorized_cli_user_profile)]
|
||||
```
|
||||
|
||||
#### Task 1.3: Protect tenant mount endpoints ✅
|
||||
**File**: `apps/cloud/src/basic_memory_cloud/api/tenant_mount.py`
|
||||
|
||||
- [x] Update import: add `AuthorizedUserProfileDep` from `..deps`
|
||||
- [x] Replace `user_profile: CurrentUserProfileHybridJwtDep` with `user_profile: AuthorizedUserProfileDep` in:
|
||||
- [x] `get_tenant_mount_info()` (line ~23)
|
||||
- [x] `create_tenant_mount_credentials()` (line ~88)
|
||||
- [x] `revoke_tenant_mount_credentials()` (line ~244)
|
||||
- [x] `list_tenant_mount_credentials()` (line ~326)
|
||||
- [x] Verify no other code changes needed (parameter name and usage stays the same)
|
||||
- [x] Run `just typecheck` to verify types
|
||||
|
||||
#### Task 1.4: Protect proxy endpoints ✅
|
||||
**File**: `apps/cloud/src/basic_memory_cloud/api/proxy.py`
|
||||
|
||||
- [x] Update import: add `AuthorizedUserProfileDep` from `..deps`
|
||||
- [x] Replace `user_profile: CurrentUserProfileHybridJwtDep` with `user_profile: AuthorizedUserProfileDep` in:
|
||||
- [x] `check_tenant_health()` (line ~21)
|
||||
- [x] `proxy_to_tenant()` (line ~63)
|
||||
- [x] Verify no other code changes needed (parameter name and usage stays the same)
|
||||
- [x] Run `just typecheck` to verify types
|
||||
|
||||
**Why Keep /proxy Architecture:**
|
||||
|
||||
The proxy layer is valuable because it:
|
||||
1. **Centralizes authorization** - Single place for JWT + subscription validation (closes both CLI and MCP auth gaps)
|
||||
2. **Handles tenant routing** - Maps tenant_id → fly_app_name without exposing infrastructure details
|
||||
3. **Abstracts infrastructure** - MCP and CLI don't need to know about Fly.io naming conventions
|
||||
4. **Enables features** - Can add rate limiting, caching, request logging, etc. at proxy layer
|
||||
5. **Supports both flows** - CLI tools and MCP tools both use /proxy endpoints
|
||||
|
||||
The extra HTTP hop is minimal (< 10ms) and worth it for architectural benefits.
|
||||
|
||||
**Performance Note:** Cloud app has Redis available - can cache subscription status to reduce database queries if needed. Initial implementation uses direct database query (simple, acceptable performance ~5-10ms).
|
||||
|
||||
#### Task 1.5: Add unit tests for subscription service
|
||||
**File**: `apps/cloud/tests/services/test_subscription_service.py` (create if doesn't exist)
|
||||
|
||||
- [ ] Create test file if it doesn't exist
|
||||
- [ ] Add test: `test_check_user_has_active_subscription_returns_true_for_active()`
|
||||
- Create user with active subscription
|
||||
- Call `check_user_has_active_subscription()`
|
||||
- Assert returns `True`
|
||||
- [ ] Add test: `test_check_user_has_active_subscription_returns_false_for_pending()`
|
||||
- Create user with pending subscription
|
||||
- Assert returns `False`
|
||||
- [ ] Add test: `test_check_user_has_active_subscription_returns_false_for_cancelled()`
|
||||
- Create user with cancelled subscription
|
||||
- Assert returns `False`
|
||||
- [ ] Add test: `test_check_user_has_active_subscription_returns_false_for_no_subscription()`
|
||||
- Create user without subscription
|
||||
- Assert returns `False`
|
||||
- [ ] Run `just test` to verify tests pass
|
||||
|
||||
#### Task 1.6: Add integration tests for dependency
|
||||
**File**: `apps/cloud/tests/test_deps.py` (create if doesn't exist)
|
||||
|
||||
- [ ] Create test file if it doesn't exist
|
||||
- [ ] Add fixtures for mocking JWT credentials
|
||||
- [ ] Add test: `test_authorized_cli_user_profile_with_active_subscription()`
|
||||
- Mock valid JWT + active subscription
|
||||
- Call dependency
|
||||
- Assert returns UserProfile
|
||||
- [ ] Add test: `test_authorized_cli_user_profile_without_subscription_raises_403()`
|
||||
- Mock valid JWT + no subscription
|
||||
- Assert raises HTTPException(403) with correct error detail
|
||||
- [ ] Add test: `test_authorized_cli_user_profile_with_inactive_subscription_raises_403()`
|
||||
- Mock valid JWT + cancelled subscription
|
||||
- Assert raises HTTPException(403)
|
||||
- [ ] Add test: `test_authorized_cli_user_profile_with_invalid_jwt_raises_401()`
|
||||
- Mock invalid JWT
|
||||
- Assert raises HTTPException(401)
|
||||
- [ ] Run `just test` to verify tests pass
|
||||
|
||||
#### Task 1.7: Deploy and verify cloud service
|
||||
- [ ] Run `just check` to verify all quality checks pass
|
||||
- [ ] Commit changes with message: "feat: add subscription validation to CLI endpoints"
|
||||
- [ ] Deploy to preview environment: `flyctl deploy --config apps/cloud/fly.toml`
|
||||
- [ ] Test manually:
|
||||
- [ ] Call `/tenant/mount/info` with valid JWT but no subscription → expect 403
|
||||
- [ ] Call `/tenant/mount/info` with valid JWT and active subscription → expect 200
|
||||
- [ ] Verify error response structure matches spec
|
||||
|
||||
### Phase 2: CLI (basic-memory)
|
||||
|
||||
#### Task 2.1: Review and understand CLI authentication flow
|
||||
**Files**: `src/basic_memory/cli/commands/cloud/`
|
||||
|
||||
- [ ] Read `core_commands.py` to understand current login flow
|
||||
- [ ] Read `api_client.py` to understand current error handling
|
||||
- [ ] Identify where 403 errors should be caught
|
||||
- [ ] Identify what error messages should be displayed
|
||||
- [ ] Document current behavior in spec if needed
|
||||
|
||||
#### Task 2.2: Update API client error handling
|
||||
**File**: `src/basic_memory/cli/commands/cloud/api_client.py`
|
||||
|
||||
- [ ] Add custom exception class `SubscriptionRequiredError` (or similar)
|
||||
- [ ] Update HTTP error handling to parse 403 responses
|
||||
- [ ] Extract `error`, `message`, and `subscribe_url` from error detail
|
||||
- [ ] Raise specific exception for subscription_required errors
|
||||
- [ ] Run `just typecheck` in basic-memory repo to verify types
|
||||
|
||||
#### Task 2.3: Update CLI login command error handling
|
||||
**File**: `src/basic_memory/cli/commands/cloud/core_commands.py`
|
||||
|
||||
- [ ] Import the subscription error exception
|
||||
- [ ] Wrap login flow with try/except for subscription errors
|
||||
- [ ] Display user-friendly error message with rich console
|
||||
- [ ] Show subscribe URL prominently
|
||||
- [ ] Provide actionable next steps
|
||||
- [ ] Run `just typecheck` to verify types
|
||||
|
||||
**Expected error handling**:
|
||||
```python
|
||||
try:
|
||||
# Existing login logic
|
||||
success = await auth.login()
|
||||
if success:
|
||||
# Test access to protected endpoint
|
||||
await api_client.test_connection()
|
||||
except SubscriptionRequiredError as e:
|
||||
console.print("\n[red]✗ Subscription Required[/red]\n")
|
||||
console.print(f"[yellow]{e.message}[/yellow]\n")
|
||||
console.print(f"Subscribe at: [blue underline]{e.subscribe_url}[/blue underline]\n")
|
||||
console.print("[dim]Once you have an active subscription, run [bold]bm cloud login[/bold] again.[/dim]")
|
||||
raise typer.Exit(1)
|
||||
```
|
||||
|
||||
#### Task 2.4: Update CLI tests
|
||||
**File**: `tests/cli/test_cloud_commands.py`
|
||||
|
||||
- [ ] Add test: `test_login_without_subscription_shows_error()`
|
||||
- Mock 403 subscription_required response
|
||||
- Call login command
|
||||
- Assert error message displayed
|
||||
- Assert subscribe URL shown
|
||||
- [ ] Add test: `test_login_with_subscription_succeeds()`
|
||||
- Mock successful authentication + subscription check
|
||||
- Call login command
|
||||
- Assert success message
|
||||
- [ ] Run `just test` to verify tests pass
|
||||
|
||||
#### Task 2.5: Update CLI documentation
|
||||
**File**: `docs/cloud-cli.md` (in basic-memory-docs repo)
|
||||
|
||||
- [ ] Add "Prerequisites" section if not present
|
||||
- [ ] Document subscription requirement
|
||||
- [ ] Add "Troubleshooting" section
|
||||
- [ ] Document "Subscription Required" error
|
||||
- [ ] Provide subscribe URL
|
||||
- [ ] Add FAQ entry about subscription errors
|
||||
- [ ] Build docs locally to verify formatting
|
||||
|
||||
### Phase 3: End-to-End Testing
|
||||
|
||||
#### Task 3.1: Create test user accounts
|
||||
**Prerequisites**: Access to WorkOS admin and database
|
||||
|
||||
- [ ] Create test user WITHOUT subscription:
|
||||
- [ ] Sign up via WorkOS AuthKit
|
||||
- [ ] Get workos_user_id from database
|
||||
- [ ] Verify no subscription record exists
|
||||
- [ ] Save credentials for testing
|
||||
- [ ] Create test user WITH active subscription:
|
||||
- [ ] Sign up via WorkOS AuthKit
|
||||
- [ ] Create subscription via Polar or dev endpoint
|
||||
- [ ] Verify subscription.status = "active" in database
|
||||
- [ ] Save credentials for testing
|
||||
|
||||
#### Task 3.2: Manual testing - User without subscription
|
||||
**Environment**: Preview/staging deployment
|
||||
|
||||
- [ ] Run `bm cloud login` with no-subscription user
|
||||
- [ ] Verify: Login shows "Subscription Required" error
|
||||
- [ ] Verify: Subscribe URL is displayed
|
||||
- [ ] Verify: Cannot run `bm cloud setup`
|
||||
- [ ] Verify: Cannot call `/tenant/mount/info` directly via curl
|
||||
- [ ] Document any issues found
|
||||
|
||||
#### Task 3.3: Manual testing - User with active subscription
|
||||
**Environment**: Preview/staging deployment
|
||||
|
||||
- [ ] Run `bm cloud login` with active-subscription user
|
||||
- [ ] Verify: Login succeeds without errors
|
||||
- [ ] Verify: Can run `bm cloud setup`
|
||||
- [ ] Verify: Can call `/tenant/mount/info` successfully
|
||||
- [ ] Verify: Can call `/proxy/*` endpoints successfully
|
||||
- [ ] Document any issues found
|
||||
|
||||
#### Task 3.4: Test subscription state transitions
|
||||
**Environment**: Preview/staging deployment + database access
|
||||
|
||||
- [ ] Start with active subscription user
|
||||
- [ ] Verify: All operations work
|
||||
- [ ] Update subscription.status to "cancelled" in database
|
||||
- [ ] Verify: Login now shows "Subscription Required" error
|
||||
- [ ] Verify: Existing tokens are rejected with 403
|
||||
- [ ] Update subscription.status back to "active"
|
||||
- [ ] Verify: Access restored immediately
|
||||
- [ ] Document any issues found
|
||||
|
||||
#### Task 3.5: Integration test suite
|
||||
**File**: `apps/cloud/tests/integration/test_cli_subscription_flow.py` (create if doesn't exist)
|
||||
|
||||
- [ ] Create integration test file
|
||||
- [ ] Add test: `test_cli_flow_without_subscription()`
|
||||
- Simulate full CLI flow without subscription
|
||||
- Assert 403 at appropriate points
|
||||
- [ ] Add test: `test_cli_flow_with_active_subscription()`
|
||||
- Simulate full CLI flow with active subscription
|
||||
- Assert all operations succeed
|
||||
- [ ] Add test: `test_subscription_expiration_blocks_access()`
|
||||
- Start with active subscription
|
||||
- Change status to cancelled
|
||||
- Assert access denied
|
||||
- [ ] Run tests in CI/CD pipeline
|
||||
- [ ] Document test coverage
|
||||
|
||||
#### Task 3.6: Load/performance testing (optional)
|
||||
**Environment**: Staging environment
|
||||
|
||||
- [ ] Test subscription check performance under load
|
||||
- [ ] Measure latency added by subscription check
|
||||
- [ ] Verify database query performance
|
||||
- [ ] Document any performance concerns
|
||||
- [ ] Optimize if needed
|
||||
|
||||
## Implementation Summary Checklist
|
||||
|
||||
Use this high-level checklist to track overall progress:
|
||||
|
||||
### Phase 1: Cloud Service 🔄
|
||||
- [x] Add subscription check method to SubscriptionService
|
||||
- [x] Add subscription validation dependency to deps.py
|
||||
- [x] Add subscription_url config (env var)
|
||||
- [x] Protect tenant mount endpoints (4 endpoints)
|
||||
- [x] Protect proxy endpoints (2 endpoints)
|
||||
- [ ] Add unit tests for subscription service
|
||||
- [ ] Add integration tests for dependency
|
||||
- [ ] Deploy and verify cloud service
|
||||
|
||||
### Phase 2: CLI Updates 🔄
|
||||
- [ ] Review CLI authentication flow
|
||||
- [ ] Update API client error handling
|
||||
- [ ] Update CLI login command error handling
|
||||
- [ ] Add CLI tests
|
||||
- [ ] Update CLI documentation
|
||||
|
||||
### Phase 3: End-to-End Testing 🧪
|
||||
- [ ] Create test user accounts
|
||||
- [ ] Manual testing - user without subscription
|
||||
- [ ] Manual testing - user with active subscription
|
||||
- [ ] Test subscription state transitions
|
||||
- [ ] Integration test suite
|
||||
- [ ] Load/performance testing (optional)
|
||||
|
||||
## Questions to Resolve
|
||||
|
||||
### Resolved ✅
|
||||
|
||||
1. **Admin Access**
|
||||
- ✅ **Decision**: Admin users bypass subscription check
|
||||
- **Rationale**: Admin endpoints already use `AdminUserHybridDep`, which is separate from CLI user endpoints
|
||||
- **Implementation**: No changes needed to admin endpoints
|
||||
|
||||
2. **Subscription Check Implementation**
|
||||
- ✅ **Decision**: Use Option A (Database Check)
|
||||
- **Rationale**: Simpler, faster to implement, works with existing infrastructure
|
||||
- **Implementation**: Single JOIN query via `get_subscription_by_workos_user_id()`
|
||||
|
||||
3. **Dependency Return Type**
|
||||
- ✅ **Decision**: Return `UserProfile` (not `UserContext`)
|
||||
- **Rationale**: Drop-in compatibility with existing endpoints, no refactoring needed
|
||||
- **Implementation**: `AuthorizedCLIUserProfileDep` returns `UserProfile`
|
||||
|
||||
### To Be Resolved ⏳
|
||||
|
||||
1. **Subscription Check Frequency**
|
||||
- **Options**:
|
||||
- Check on every API call (slower, more secure) ✅ **RECOMMENDED**
|
||||
- Cache subscription status (faster, risk of stale data)
|
||||
- Check only on login/setup (fast, but allows expired subscriptions temporarily)
|
||||
- **Recommendation**: Check on every call via dependency injection (simple, secure, acceptable performance)
|
||||
- **Impact**: ~5-10ms per request (single indexed JOIN query)
|
||||
|
||||
2. **Grace Period**
|
||||
- **Options**:
|
||||
- No grace period - immediate block when status != "active" ✅ **RECOMMENDED**
|
||||
- 7-day grace period after period_end
|
||||
- 14-day grace period after period_end
|
||||
- **Recommendation**: No grace period initially, add later if needed based on customer feedback
|
||||
- **Implementation**: Check `subscription.status == "active"` only (ignore period_end initially)
|
||||
|
||||
3. **Subscription Expiration Handling**
|
||||
- **Question**: Should we check `current_period_end < now()` in addition to `status == "active"`?
|
||||
- **Options**:
|
||||
- Only check status field (rely on Polar webhooks to update status) ✅ **RECOMMENDED**
|
||||
- Check both status and current_period_end (more defensive)
|
||||
- **Recommendation**: Only check status field, assume Polar webhooks keep it current
|
||||
- **Risk**: If webhooks fail, expired subscriptions might retain access until webhook succeeds
|
||||
|
||||
4. **Subscribe URL**
|
||||
- **Question**: What's the actual subscription URL?
|
||||
- **Current**: Spec uses `https://basicmemory.com/subscribe`
|
||||
- **Action Required**: Verify correct URL before implementation
|
||||
|
||||
5. **Dev Mode / Testing Bypass**
|
||||
- **Question**: Support bypass for development/testing?
|
||||
- **Options**:
|
||||
- Environment variable: `DISABLE_SUBSCRIPTION_CHECK=true`
|
||||
- Always enforce (more realistic testing) ✅ **RECOMMENDED**
|
||||
- **Recommendation**: No bypass - use test users with real subscriptions for realistic testing
|
||||
- **Implementation**: Create dev endpoint to activate subscriptions for testing
|
||||
|
||||
## Related Specs
|
||||
|
||||
- SPEC-9: Multi-Project Bidirectional Sync Architecture (CLI affected by this change)
|
||||
- SPEC-8: TigrisFS Integration (Mount endpoints protected)
|
||||
|
||||
## Notes
|
||||
|
||||
- This spec prioritizes security over convenience - better to block unauthorized access than risk revenue loss
|
||||
- Clear error messages are critical - users should understand why they're blocked and how to resolve it
|
||||
- Consider adding telemetry to track subscription_required errors for monitoring signup conversion
|
||||
@@ -0,0 +1,210 @@
|
||||
---
|
||||
title: 'SPEC-14: Cloud Git Versioning & GitHub Backup'
|
||||
type: spec
|
||||
permalink: specs/spec-14-cloud-git-versioning
|
||||
tags:
|
||||
- git
|
||||
- github
|
||||
- backup
|
||||
- versioning
|
||||
- cloud
|
||||
related:
|
||||
- specs/spec-9-multi-project-bisync
|
||||
- specs/spec-9-follow-ups-conflict-sync-and-observability
|
||||
status: deferred
|
||||
---
|
||||
|
||||
# SPEC-14: Cloud Git Versioning & GitHub Backup
|
||||
|
||||
**Status: DEFERRED** - Postponed until multi-user/teams feature development. Using S3 versioning (SPEC-9.1) for v1 instead.
|
||||
|
||||
## Why Deferred
|
||||
|
||||
**Original goals can be met with simpler solutions:**
|
||||
- Version history → **S3 bucket versioning** (automatic, zero config)
|
||||
- Offsite backup → **Tigris global replication** (built-in)
|
||||
- Restore capability → **S3 version restore** (`bm cloud restore --version-id`)
|
||||
- Collaboration → **Deferred to teams/multi-user feature** (not v1 requirement)
|
||||
|
||||
**Complexity vs value trade-off:**
|
||||
- Git integration adds: committer service, puller service, webhooks, LFS, merge conflicts
|
||||
- Risk: Loop detection between Git ↔ rclone bisync ↔ local edits
|
||||
- S3 versioning gives 80% of value with 5% of complexity
|
||||
|
||||
**When to revisit:**
|
||||
- Teams/multi-user features (PR-based collaboration workflow)
|
||||
- User requests for commit messages and branch-based workflows
|
||||
- Need for fine-grained audit trail beyond S3 object metadata
|
||||
|
||||
---
|
||||
|
||||
## Original Specification (for reference)
|
||||
|
||||
## Why
|
||||
Early access users want **transparent version history**, easy **offsite backup**, and a familiar **restore/branching** workflow. Git/GitHub integration would provide:
|
||||
- Auditable history of every change (who/when/why)
|
||||
- Branches/PRs for review and collaboration
|
||||
- Offsite private backup under the user's control
|
||||
- Escape hatch: users can always `git clone` their knowledge base
|
||||
|
||||
**Note:** These goals are now addressed via S3 versioning (SPEC-9.1) for single-user use case.
|
||||
|
||||
## Goals
|
||||
- **Transparent**: Users keep using Basic Memory; Git runs behind the scenes.
|
||||
- **Private**: Push to a **private GitHub repo** that the user owns (or tenant org).
|
||||
- **Reliable**: No data loss, deterministic mapping of filesystem ↔ Git.
|
||||
- **Composable**: Plays nicely with SPEC‑9 bisync and upcoming conflict features (SPEC‑9 Follow‑Ups).
|
||||
|
||||
**Non‑Goals (for v1):**
|
||||
- Fine‑grained per‑file encryption in Git history (can be layered later).
|
||||
- Large media optimization beyond Git LFS defaults.
|
||||
|
||||
## User Stories
|
||||
1. *As a user*, I connect my GitHub and choose a private backup repo.
|
||||
2. *As a user*, every change I make in cloud (or via bisync) is **committed** and **pushed** automatically.
|
||||
3. *As a user*, I can **restore** a file/folder/project to a prior version.
|
||||
4. *As a power user*, I can **git pull/push** directly to collaborate outside the app.
|
||||
5. *As an admin*, I can enforce repo ownership (tenant org) and least‑privilege scopes.
|
||||
|
||||
## Scope
|
||||
- **In scope:** Full repo backup of `/app/data/` (all projects) with optional selective subpaths.
|
||||
- **Out of scope (v1):** Partial shallow mirrors; encrypted Git; cross‑provider SCM (GitLab/Bitbucket).
|
||||
|
||||
## Architecture
|
||||
### Topology
|
||||
- **Authoritative working tree**: `/app/data/` (bucket mount) remains the source of truth (SPEC‑9).
|
||||
- **Bare repo** lives alongside: `/app/git/${tenant}/knowledge.git` (server‑side).
|
||||
- **Mirror remote**: `github.com/<owner>/<repo>.git` (private).
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[/Users & Agents/] -->|writes/edits| B[/app/data/]
|
||||
B -->|file events| C[Committer Service]
|
||||
C -->|git commit| D[(Bare Repo)]
|
||||
D -->|push| E[(GitHub Private Repo)]
|
||||
E -->|webhook (push)| F[Puller Service]
|
||||
F -->|git pull/merge| D
|
||||
D -->|checkout/merge| B
|
||||
```
|
||||
|
||||
### Services
|
||||
- **Committer Service** (daemon):
|
||||
- Watches `/app/data/` for changes (inotify/poll)
|
||||
- Batches changes (debounce e.g. 2–5s)
|
||||
- Writes `.bmmeta` (if present) into commit message trailer (see Follow‑Ups)
|
||||
- `git add -A && git commit -m "chore(sync): <summary>
|
||||
|
||||
BM-Meta: <json>"`
|
||||
- Periodic `git push` to GitHub mirror (configurable interval)
|
||||
- **Puller Service** (webhook target):
|
||||
- Receives GitHub webhook (push) → `git fetch`
|
||||
- **Fast‑forward** merges to `main` only; reject non‑FF unless policy allows
|
||||
- Applies changes back to `/app/data/` via clean checkout
|
||||
- Emits sync events for Basic Memory indexers
|
||||
|
||||
### Auth & Security
|
||||
- **GitHub App** (recommended): minimal scopes: `contents:read/write`, `metadata:read`, webhook.
|
||||
- Tenant‑scoped installation; repo created in user account or tenant org.
|
||||
- Tokens stored in KMS/secret manager; rotated automatically.
|
||||
- Optional policy: allow only **FF merges** on `main`; non‑FF requires PR.
|
||||
|
||||
### Repo Layout
|
||||
- **Monorepo** (default): one repo per tenant mirrors `/app/data/` with subfolders per project.
|
||||
- Optional multi‑repo mode (later): one repo per project.
|
||||
|
||||
### File Handling
|
||||
- Honor `.gitignore` generated from `.bmignore.rclone` + BM defaults (cache, temp, state).
|
||||
- **Git LFS** for large binaries (images, media) — auto track by extension/size threshold.
|
||||
- Normalize newline + Unicode (aligns with Follow‑Ups).
|
||||
|
||||
### Conflict Model
|
||||
- **Primary concurrency**: SPEC‑9 Follow‑Ups (`.bmmeta`, conflict copies) stays the first line of defense.
|
||||
- **Git merges** are a **secondary** mechanism:
|
||||
- Server only auto‑merges **text** conflicts when trivial (FF or clean 3‑way).
|
||||
- Otherwise, create `name (conflict from <branch>, <ts>).md` and surface via events.
|
||||
|
||||
### Data Flow vs Bisync
|
||||
- Bisync (rclone) continues between local sync dir ↔ bucket.
|
||||
- Git sits **cloud‑side** between bucket and GitHub.
|
||||
- On **pull** from GitHub → files written to `/app/data/` → picked up by indexers & eventually by bisync back to users.
|
||||
|
||||
## CLI & UX
|
||||
New commands (cloud mode):
|
||||
- `bm cloud git connect` — Launch GitHub App installation; create private repo; store installation id.
|
||||
- `bm cloud git status` — Show connected repo, last push time, last webhook delivery, pending commits.
|
||||
- `bm cloud git push` — Manual push (rarely needed).
|
||||
- `bm cloud git pull` — Manual pull/FF (admin only by default).
|
||||
- `bm cloud snapshot -m "message"` — Create a tagged point‑in‑time snapshot (git tag).
|
||||
- `bm restore <path> --to <commit|tag>` — Restore file/folder/project to prior version.
|
||||
|
||||
Settings:
|
||||
- `bm config set git.autoPushInterval=5s`
|
||||
- `bm config set git.lfs.sizeThreshold=10MB`
|
||||
- `bm config set git.allowNonFF=false`
|
||||
|
||||
## Migration & Backfill
|
||||
- On connect, if repo empty: initial commit of entire `/app/data/`.
|
||||
- If repo has content: require **one‑time import** path (clone to staging, reconcile, choose direction).
|
||||
|
||||
## Edge Cases
|
||||
- Massive deletes: gated by SPEC‑9 `max_delete` **and** Git pre‑push hook checks.
|
||||
- Case changes and rename detection: rely on git rename heuristics + Follow‑Ups move hints.
|
||||
- Secrets: default ignore common secret patterns; allow custom deny list.
|
||||
|
||||
## Telemetry & Observability
|
||||
- Emit `git_commit`, `git_push`, `git_pull`, `git_conflict` events with correlation IDs.
|
||||
- `bm sync --report` extended with Git stats (commit count, delta bytes, push latency).
|
||||
|
||||
## Phased Plan
|
||||
### Phase 0 — Prototype (1 sprint)
|
||||
- Server: bare repo init + simple committer (batch every 10s) + manual GitHub token.
|
||||
- CLI: `bm cloud git connect --token <PAT>` (dev‑only)
|
||||
- Success: edits in `/app/data/` appear in GitHub within 30s.
|
||||
|
||||
### Phase 1 — GitHub App & Webhooks (1–2 sprints)
|
||||
- Switch to GitHub App installs; create private repo; store installation id.
|
||||
- Committer hardened (debounce 2–5s, backoff, retries).
|
||||
- Puller service with webhook → FF merge → checkout to `/app/data/`.
|
||||
- LFS auto‑track + `.gitignore` generation.
|
||||
- CLI surfaces status + logs.
|
||||
|
||||
### Phase 2 — Restore & Snapshots (1 sprint)
|
||||
- `bm restore` for file/folder/project with dry‑run.
|
||||
- `bm cloud snapshot` tags + list/inspect.
|
||||
- Policy: PR‑only non‑FF, admin override.
|
||||
|
||||
### Phase 3 — Selective & Multi‑Repo (nice‑to‑have)
|
||||
- Include/exclude projects; optional per‑project repos.
|
||||
- Advanced policies (branch protections, required reviews).
|
||||
|
||||
## Acceptance Criteria
|
||||
- Changes to `/app/data/` are committed and pushed automatically within configurable interval (default ≤5s).
|
||||
- GitHub webhook pull results in updated files in `/app/data/` (FF‑only by default).
|
||||
- LFS configured and functioning; large files don't bloat history.
|
||||
- `bm cloud git status` shows connected repo and last push/pull times.
|
||||
- `bm restore` restores a file/folder to a prior commit with a clear audit trail.
|
||||
- End‑to‑end works alongside SPEC‑9 bisync without loops or data loss.
|
||||
|
||||
## Risks & Mitigations
|
||||
- **Loop risk (Git ↔ Bisync)**: Writes to `/app/data/` → bisync → local → user edits → back again. *Mitigation*: Debounce, commit squashing, idempotent `.bmmeta` versioning, and watch exclusion windows during pull.
|
||||
- **Repo bloat**: Lots of binary churn. *Mitigation*: default LFS, size threshold, optional media‑only repo later.
|
||||
- **Security**: Token leakage. *Mitigation*: GitHub App with short‑lived tokens, KMS storage, scoped permissions.
|
||||
- **Merge complexity**: Non‑trivial conflicts. *Mitigation*: prefer FF; otherwise conflict copies + events; require PR for non‑FF.
|
||||
|
||||
## Open Questions
|
||||
- Do we default to **monorepo** per tenant, or offer project‑per‑repo at connect time?
|
||||
- Should `restore` write to a branch and open a PR, or directly modify `main`?
|
||||
- How do we expose Git history in UI (timeline view) without users dropping to CLI?
|
||||
|
||||
## Appendix: Sample Config
|
||||
```json
|
||||
{
|
||||
"git": {
|
||||
"enabled": true,
|
||||
"repo": "https://github.com/<owner>/<repo>.git",
|
||||
"autoPushInterval": "5s",
|
||||
"allowNonFF": false,
|
||||
"lfs": { "sizeThreshold": 10485760 }
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,210 @@
|
||||
---
|
||||
title: 'SPEC-14: Cloud Git Versioning & GitHub Backup'
|
||||
type: spec
|
||||
permalink: specs/spec-14-cloud-git-versioning
|
||||
tags:
|
||||
- git
|
||||
- github
|
||||
- backup
|
||||
- versioning
|
||||
- cloud
|
||||
related:
|
||||
- specs/spec-9-multi-project-bisync
|
||||
- specs/spec-9-follow-ups-conflict-sync-and-observability
|
||||
status: deferred
|
||||
---
|
||||
|
||||
# SPEC-14: Cloud Git Versioning & GitHub Backup
|
||||
|
||||
**Status: DEFERRED** - Postponed until multi-user/teams feature development. Using S3 versioning (SPEC-9.1) for v1 instead.
|
||||
|
||||
## Why Deferred
|
||||
|
||||
**Original goals can be met with simpler solutions:**
|
||||
- Version history → **S3 bucket versioning** (automatic, zero config)
|
||||
- Offsite backup → **Tigris global replication** (built-in)
|
||||
- Restore capability → **S3 version restore** (`bm cloud restore --version-id`)
|
||||
- Collaboration → **Deferred to teams/multi-user feature** (not v1 requirement)
|
||||
|
||||
**Complexity vs value trade-off:**
|
||||
- Git integration adds: committer service, puller service, webhooks, LFS, merge conflicts
|
||||
- Risk: Loop detection between Git ↔ rclone bisync ↔ local edits
|
||||
- S3 versioning gives 80% of value with 5% of complexity
|
||||
|
||||
**When to revisit:**
|
||||
- Teams/multi-user features (PR-based collaboration workflow)
|
||||
- User requests for commit messages and branch-based workflows
|
||||
- Need for fine-grained audit trail beyond S3 object metadata
|
||||
|
||||
---
|
||||
|
||||
## Original Specification (for reference)
|
||||
|
||||
## Why
|
||||
Early access users want **transparent version history**, easy **offsite backup**, and a familiar **restore/branching** workflow. Git/GitHub integration would provide:
|
||||
- Auditable history of every change (who/when/why)
|
||||
- Branches/PRs for review and collaboration
|
||||
- Offsite private backup under the user's control
|
||||
- Escape hatch: users can always `git clone` their knowledge base
|
||||
|
||||
**Note:** These goals are now addressed via S3 versioning (SPEC-9.1) for single-user use case.
|
||||
|
||||
## Goals
|
||||
- **Transparent**: Users keep using Basic Memory; Git runs behind the scenes.
|
||||
- **Private**: Push to a **private GitHub repo** that the user owns (or tenant org).
|
||||
- **Reliable**: No data loss, deterministic mapping of filesystem ↔ Git.
|
||||
- **Composable**: Plays nicely with SPEC‑9 bisync and upcoming conflict features (SPEC‑9 Follow‑Ups).
|
||||
|
||||
**Non‑Goals (for v1):**
|
||||
- Fine‑grained per‑file encryption in Git history (can be layered later).
|
||||
- Large media optimization beyond Git LFS defaults.
|
||||
|
||||
## User Stories
|
||||
1. *As a user*, I connect my GitHub and choose a private backup repo.
|
||||
2. *As a user*, every change I make in cloud (or via bisync) is **committed** and **pushed** automatically.
|
||||
3. *As a user*, I can **restore** a file/folder/project to a prior version.
|
||||
4. *As a power user*, I can **git pull/push** directly to collaborate outside the app.
|
||||
5. *As an admin*, I can enforce repo ownership (tenant org) and least‑privilege scopes.
|
||||
|
||||
## Scope
|
||||
- **In scope:** Full repo backup of `/app/data/` (all projects) with optional selective subpaths.
|
||||
- **Out of scope (v1):** Partial shallow mirrors; encrypted Git; cross‑provider SCM (GitLab/Bitbucket).
|
||||
|
||||
## Architecture
|
||||
### Topology
|
||||
- **Authoritative working tree**: `/app/data/` (bucket mount) remains the source of truth (SPEC‑9).
|
||||
- **Bare repo** lives alongside: `/app/git/${tenant}/knowledge.git` (server‑side).
|
||||
- **Mirror remote**: `github.com/<owner>/<repo>.git` (private).
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[/Users & Agents/] -->|writes/edits| B[/app/data/]
|
||||
B -->|file events| C[Committer Service]
|
||||
C -->|git commit| D[(Bare Repo)]
|
||||
D -->|push| E[(GitHub Private Repo)]
|
||||
E -->|webhook (push)| F[Puller Service]
|
||||
F -->|git pull/merge| D
|
||||
D -->|checkout/merge| B
|
||||
```
|
||||
|
||||
### Services
|
||||
- **Committer Service** (daemon):
|
||||
- Watches `/app/data/` for changes (inotify/poll)
|
||||
- Batches changes (debounce e.g. 2–5s)
|
||||
- Writes `.bmmeta` (if present) into commit message trailer (see Follow‑Ups)
|
||||
- `git add -A && git commit -m "chore(sync): <summary>
|
||||
|
||||
BM-Meta: <json>"`
|
||||
- Periodic `git push` to GitHub mirror (configurable interval)
|
||||
- **Puller Service** (webhook target):
|
||||
- Receives GitHub webhook (push) → `git fetch`
|
||||
- **Fast‑forward** merges to `main` only; reject non‑FF unless policy allows
|
||||
- Applies changes back to `/app/data/` via clean checkout
|
||||
- Emits sync events for Basic Memory indexers
|
||||
|
||||
### Auth & Security
|
||||
- **GitHub App** (recommended): minimal scopes: `contents:read/write`, `metadata:read`, webhook.
|
||||
- Tenant‑scoped installation; repo created in user account or tenant org.
|
||||
- Tokens stored in KMS/secret manager; rotated automatically.
|
||||
- Optional policy: allow only **FF merges** on `main`; non‑FF requires PR.
|
||||
|
||||
### Repo Layout
|
||||
- **Monorepo** (default): one repo per tenant mirrors `/app/data/` with subfolders per project.
|
||||
- Optional multi‑repo mode (later): one repo per project.
|
||||
|
||||
### File Handling
|
||||
- Honor `.gitignore` generated from `.bmignore.rclone` + BM defaults (cache, temp, state).
|
||||
- **Git LFS** for large binaries (images, media) — auto track by extension/size threshold.
|
||||
- Normalize newline + Unicode (aligns with Follow‑Ups).
|
||||
|
||||
### Conflict Model
|
||||
- **Primary concurrency**: SPEC‑9 Follow‑Ups (`.bmmeta`, conflict copies) stays the first line of defense.
|
||||
- **Git merges** are a **secondary** mechanism:
|
||||
- Server only auto‑merges **text** conflicts when trivial (FF or clean 3‑way).
|
||||
- Otherwise, create `name (conflict from <branch>, <ts>).md` and surface via events.
|
||||
|
||||
### Data Flow vs Bisync
|
||||
- Bisync (rclone) continues between local sync dir ↔ bucket.
|
||||
- Git sits **cloud‑side** between bucket and GitHub.
|
||||
- On **pull** from GitHub → files written to `/app/data/` → picked up by indexers & eventually by bisync back to users.
|
||||
|
||||
## CLI & UX
|
||||
New commands (cloud mode):
|
||||
- `bm cloud git connect` — Launch GitHub App installation; create private repo; store installation id.
|
||||
- `bm cloud git status` — Show connected repo, last push time, last webhook delivery, pending commits.
|
||||
- `bm cloud git push` — Manual push (rarely needed).
|
||||
- `bm cloud git pull` — Manual pull/FF (admin only by default).
|
||||
- `bm cloud snapshot -m "message"` — Create a tagged point‑in‑time snapshot (git tag).
|
||||
- `bm restore <path> --to <commit|tag>` — Restore file/folder/project to prior version.
|
||||
|
||||
Settings:
|
||||
- `bm config set git.autoPushInterval=5s`
|
||||
- `bm config set git.lfs.sizeThreshold=10MB`
|
||||
- `bm config set git.allowNonFF=false`
|
||||
|
||||
## Migration & Backfill
|
||||
- On connect, if repo empty: initial commit of entire `/app/data/`.
|
||||
- If repo has content: require **one‑time import** path (clone to staging, reconcile, choose direction).
|
||||
|
||||
## Edge Cases
|
||||
- Massive deletes: gated by SPEC‑9 `max_delete` **and** Git pre‑push hook checks.
|
||||
- Case changes and rename detection: rely on git rename heuristics + Follow‑Ups move hints.
|
||||
- Secrets: default ignore common secret patterns; allow custom deny list.
|
||||
|
||||
## Telemetry & Observability
|
||||
- Emit `git_commit`, `git_push`, `git_pull`, `git_conflict` events with correlation IDs.
|
||||
- `bm sync --report` extended with Git stats (commit count, delta bytes, push latency).
|
||||
|
||||
## Phased Plan
|
||||
### Phase 0 — Prototype (1 sprint)
|
||||
- Server: bare repo init + simple committer (batch every 10s) + manual GitHub token.
|
||||
- CLI: `bm cloud git connect --token <PAT>` (dev‑only)
|
||||
- Success: edits in `/app/data/` appear in GitHub within 30s.
|
||||
|
||||
### Phase 1 — GitHub App & Webhooks (1–2 sprints)
|
||||
- Switch to GitHub App installs; create private repo; store installation id.
|
||||
- Committer hardened (debounce 2–5s, backoff, retries).
|
||||
- Puller service with webhook → FF merge → checkout to `/app/data/`.
|
||||
- LFS auto‑track + `.gitignore` generation.
|
||||
- CLI surfaces status + logs.
|
||||
|
||||
### Phase 2 — Restore & Snapshots (1 sprint)
|
||||
- `bm restore` for file/folder/project with dry‑run.
|
||||
- `bm cloud snapshot` tags + list/inspect.
|
||||
- Policy: PR‑only non‑FF, admin override.
|
||||
|
||||
### Phase 3 — Selective & Multi‑Repo (nice‑to‑have)
|
||||
- Include/exclude projects; optional per‑project repos.
|
||||
- Advanced policies (branch protections, required reviews).
|
||||
|
||||
## Acceptance Criteria
|
||||
- Changes to `/app/data/` are committed and pushed automatically within configurable interval (default ≤5s).
|
||||
- GitHub webhook pull results in updated files in `/app/data/` (FF‑only by default).
|
||||
- LFS configured and functioning; large files don't bloat history.
|
||||
- `bm cloud git status` shows connected repo and last push/pull times.
|
||||
- `bm restore` restores a file/folder to a prior commit with a clear audit trail.
|
||||
- End‑to‑end works alongside SPEC‑9 bisync without loops or data loss.
|
||||
|
||||
## Risks & Mitigations
|
||||
- **Loop risk (Git ↔ Bisync)**: Writes to `/app/data/` → bisync → local → user edits → back again. *Mitigation*: Debounce, commit squashing, idempotent `.bmmeta` versioning, and watch exclusion windows during pull.
|
||||
- **Repo bloat**: Lots of binary churn. *Mitigation*: default LFS, size threshold, optional media‑only repo later.
|
||||
- **Security**: Token leakage. *Mitigation*: GitHub App with short‑lived tokens, KMS storage, scoped permissions.
|
||||
- **Merge complexity**: Non‑trivial conflicts. *Mitigation*: prefer FF; otherwise conflict copies + events; require PR for non‑FF.
|
||||
|
||||
## Open Questions
|
||||
- Do we default to **monorepo** per tenant, or offer project‑per‑repo at connect time?
|
||||
- Should `restore` write to a branch and open a PR, or directly modify `main`?
|
||||
- How do we expose Git history in UI (timeline view) without users dropping to CLI?
|
||||
|
||||
## Appendix: Sample Config
|
||||
```json
|
||||
{
|
||||
"git": {
|
||||
"enabled": true,
|
||||
"repo": "https://github.com/<owner>/<repo>.git",
|
||||
"autoPushInterval": "5s",
|
||||
"allowNonFF": false,
|
||||
"lfs": { "sizeThreshold": 10485760 }
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,273 @@
|
||||
---
|
||||
title: 'SPEC-15: Configuration Persistence via Tigris for Cloud Tenants'
|
||||
type: spec
|
||||
permalink: specs/spec-14-config-persistence-tigris
|
||||
tags:
|
||||
- persistence
|
||||
- tigris
|
||||
- multi-tenant
|
||||
- infrastructure
|
||||
- configuration
|
||||
status: draft
|
||||
---
|
||||
|
||||
# SPEC-15: Configuration Persistence via Tigris for Cloud Tenants
|
||||
|
||||
## Why
|
||||
|
||||
We need to persist Basic Memory configuration across Fly.io deployments without using persistent volumes or external databases.
|
||||
|
||||
**Current Problems:**
|
||||
- `~/.basic-memory/config.json` lost on every deployment (project configuration)
|
||||
- `~/.basic-memory/memory.db` lost on every deployment (search index)
|
||||
- Persistent volumes break clean deployment workflow
|
||||
- External databases (Turso) require per-tenant token management
|
||||
|
||||
**The Insight:**
|
||||
The SQLite database is just an **index cache** of the markdown files. It can be rebuilt in seconds from the source markdown files in Tigris. Only the small `config.json` file needs true persistence.
|
||||
|
||||
**Solution:**
|
||||
- Store `config.json` in Tigris bucket (persistent, small file)
|
||||
- Rebuild `memory.db` on startup from markdown files (fast, ephemeral)
|
||||
- No persistent volumes, no external databases, no token management
|
||||
|
||||
## What
|
||||
|
||||
Store Basic Memory configuration in the Tigris bucket and rebuild the database index on tenant machine startup.
|
||||
|
||||
**Affected Components:**
|
||||
- `basic-memory/src/basic_memory/config.py` - Add configurable config directory
|
||||
|
||||
**Architecture:**
|
||||
|
||||
```bash
|
||||
# Tigris Bucket (persistent, mounted at /app/data)
|
||||
/app/data/
|
||||
├── .basic-memory/
|
||||
│ └── config.json # ← Project configuration (persistent, accessed via BASIC_MEMORY_CONFIG_DIR)
|
||||
└── basic-memory/ # ← Markdown files (persistent, BASIC_MEMORY_HOME)
|
||||
├── project1/
|
||||
└── project2/
|
||||
|
||||
# Fly Machine (ephemeral)
|
||||
/app/.basic-memory/
|
||||
└── memory.db # ← Rebuilt on startup (fast local disk)
|
||||
```
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### 1. Add Configurable Config Directory to Basic Memory
|
||||
|
||||
Currently `ConfigManager` hardcodes `~/.basic-memory/config.json`. Add environment variable to override:
|
||||
|
||||
```python
|
||||
# basic-memory/src/basic_memory/config.py
|
||||
|
||||
class ConfigManager:
|
||||
"""Manages Basic Memory configuration."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the configuration manager."""
|
||||
home = os.getenv("HOME", Path.home())
|
||||
if isinstance(home, str):
|
||||
home = Path(home)
|
||||
|
||||
# Allow override via environment variable
|
||||
if config_dir := os.getenv("BASIC_MEMORY_CONFIG_DIR"):
|
||||
self.config_dir = Path(config_dir)
|
||||
else:
|
||||
self.config_dir = home / DATA_DIR_NAME
|
||||
|
||||
self.config_file = self.config_dir / CONFIG_FILE_NAME
|
||||
|
||||
# Ensure config directory exists
|
||||
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||
```
|
||||
|
||||
### 2. Rebuild Database on Startup
|
||||
|
||||
Basic Memory already has the sync functionality. Just ensure it runs on startup:
|
||||
|
||||
```python
|
||||
# apps/api/src/basic_memory_cloud_api/main.py
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_sync():
|
||||
"""Rebuild database index from Tigris markdown files."""
|
||||
logger.info("Starting database rebuild from Tigris")
|
||||
|
||||
# Initialize file sync (rebuilds index from markdown files)
|
||||
app_config = ConfigManager().config
|
||||
await initialize_file_sync(app_config)
|
||||
|
||||
logger.info("Database rebuild complete")
|
||||
```
|
||||
|
||||
### 3. Environment Configuration
|
||||
|
||||
```bash
|
||||
# Machine environment variables
|
||||
BASIC_MEMORY_CONFIG_DIR=/app/data/.basic-memory # Config read/written directly to Tigris
|
||||
# memory.db stays in default location: /app/.basic-memory/memory.db (local ephemeral disk)
|
||||
```
|
||||
|
||||
## Implementation Task List
|
||||
|
||||
### Phase 1: Basic Memory Changes ✅
|
||||
- [x] Add `BASIC_MEMORY_CONFIG_DIR` environment variable support to `ConfigManager.__init__()`
|
||||
- [x] Test config loading from custom directory
|
||||
- [x] Update tests to verify custom config dir works
|
||||
|
||||
### Phase 2: Tigris Bucket Structure ✅
|
||||
- [x] Ensure `.basic-memory/` directory exists in Tigris bucket on tenant creation
|
||||
- ✅ ConfigManager auto-creates on first run, no explicit provisioning needed
|
||||
- [x] Initialize `config.json` in Tigris on first tenant deployment
|
||||
- ✅ ConfigManager creates config.json automatically in BASIC_MEMORY_CONFIG_DIR
|
||||
- [x] Verify TigrisFS handles hidden directories correctly
|
||||
- ✅ TigrisFS supports hidden directories (verified in SPEC-8)
|
||||
|
||||
### Phase 3: Deployment Integration ✅
|
||||
- [x] Set `BASIC_MEMORY_CONFIG_DIR` environment variable in machine deployment
|
||||
- ✅ Added to BasicMemoryMachineConfigBuilder in fly_schemas.py
|
||||
- [x] Ensure database rebuild runs on machine startup via initialization sync
|
||||
- ✅ sync_worker.py runs initialize_file_sync every 30s (already implemented)
|
||||
- [x] Handle first-time tenant setup (no config exists yet)
|
||||
- ✅ ConfigManager creates config.json on first initialization
|
||||
- [ ] Test deployment workflow with config persistence
|
||||
|
||||
### Phase 4: Testing
|
||||
- [x] Unit tests for config directory override
|
||||
- [-] Integration test: deploy → write config → redeploy → verify config persists
|
||||
- [ ] Integration test: deploy → add project → redeploy → verify project in config
|
||||
- [ ] Performance test: measure db rebuild time on startup
|
||||
|
||||
### Phase 5: Documentation
|
||||
- [ ] Document config persistence architecture
|
||||
- [ ] Update deployment runbook
|
||||
- [ ] Document startup sequence and timing
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. **Config Persistence**
|
||||
- [ ] config.json persists across deployments
|
||||
- [ ] Projects list maintained across restarts
|
||||
- [ ] No manual configuration needed after redeploy
|
||||
|
||||
2. **Database Rebuild**
|
||||
- [ ] memory.db rebuilt on startup in < 30 seconds
|
||||
- [ ] All entities indexed correctly
|
||||
- [ ] Search functionality works after rebuild
|
||||
|
||||
3. **Performance**
|
||||
- [ ] SQLite queries remain fast (local disk)
|
||||
- [ ] Config reads acceptable (symlink to Tigris)
|
||||
- [ ] No noticeable performance degradation
|
||||
|
||||
4. **Deployment Workflow**
|
||||
- [ ] Clean deployments without volumes
|
||||
- [ ] No new external dependencies
|
||||
- [ ] No secret management needed
|
||||
|
||||
### Testing Procedure
|
||||
|
||||
1. **Config Persistence Test**
|
||||
```bash
|
||||
# Deploy tenant
|
||||
POST /tenants → tenant_id
|
||||
|
||||
# Add a project
|
||||
basic-memory project add "test-project" ~/test
|
||||
|
||||
# Verify config has project
|
||||
cat /app/data/.basic-memory/config.json
|
||||
|
||||
# Redeploy machine
|
||||
fly deploy --app basic-memory-{tenant_id}
|
||||
|
||||
# Verify project still exists
|
||||
basic-memory project list
|
||||
```
|
||||
|
||||
2. **Database Rebuild Test**
|
||||
```bash
|
||||
# Create notes
|
||||
basic-memory write "Test Note" --content "..."
|
||||
|
||||
# Redeploy (db lost)
|
||||
fly deploy --app basic-memory-{tenant_id}
|
||||
|
||||
# Wait for startup sync
|
||||
sleep 10
|
||||
|
||||
# Verify note is indexed
|
||||
basic-memory search "Test Note"
|
||||
```
|
||||
|
||||
3. **Performance Benchmark**
|
||||
```bash
|
||||
# Time the startup sync
|
||||
time basic-memory sync
|
||||
|
||||
# Should be < 30 seconds for typical tenant
|
||||
```
|
||||
|
||||
## Benefits Over Alternatives
|
||||
|
||||
**vs. Persistent Volumes:**
|
||||
- ✅ Clean deployment workflow
|
||||
- ✅ No volume migration needed
|
||||
- ✅ Simpler infrastructure
|
||||
|
||||
**vs. Turso (External Database):**
|
||||
- ✅ No per-tenant token management
|
||||
- ✅ No external service dependencies
|
||||
- ✅ No additional costs
|
||||
- ✅ Simpler architecture
|
||||
|
||||
**vs. SQLite on FUSE:**
|
||||
- ✅ Fast local SQLite performance
|
||||
- ✅ Only slow reads for small config file
|
||||
- ✅ Database queries remain fast
|
||||
|
||||
## Implementation Assignment
|
||||
|
||||
**Primary Agent:** `python-developer`
|
||||
- Add `BASIC_MEMORY_CONFIG_DIR` environment variable to ConfigManager
|
||||
- Update deployment workflow to set environment variable
|
||||
- Ensure startup sync runs correctly
|
||||
|
||||
**Review Agent:** `system-architect`
|
||||
- Validate architecture simplicity
|
||||
- Review performance implications
|
||||
- Assess startup timing
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Internal:** TigrisFS must be working and stable
|
||||
- **Internal:** Basic Memory sync must be reliable
|
||||
- **Internal:** SPEC-8 (TigrisFS Integration) must be complete
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should we add a health check that waits for db rebuild to complete?
|
||||
2. Do we need to handle very large knowledge bases (>10k entities) differently?
|
||||
3. Should we add metrics for startup sync duration?
|
||||
|
||||
## References
|
||||
|
||||
- Basic Memory sync: `basic-memory/src/basic_memory/services/initialization.py`
|
||||
- Config management: `basic-memory/src/basic_memory/config.py`
|
||||
- TigrisFS integration: SPEC-8
|
||||
|
||||
---
|
||||
|
||||
**Status Updates:**
|
||||
|
||||
- 2025-10-08: Pivoted from Turso to Tigris-based config persistence
|
||||
- 2025-10-08: Phase 1 complete - BASIC_MEMORY_CONFIG_DIR support added (PR #343)
|
||||
- 2025-10-08: Phases 2-3 complete - Added BASIC_MEMORY_CONFIG_DIR to machine config
|
||||
- Config now persists to /app/data/.basic-memory/config.json in Tigris bucket
|
||||
- Database rebuild already working via sync_worker.py
|
||||
- Ready for deployment testing (Phase 4)
|
||||
@@ -0,0 +1,800 @@
|
||||
---
|
||||
title: 'SPEC-16: MCP Cloud Service Consolidation'
|
||||
type: spec
|
||||
permalink: specs/spec-16-mcp-cloud-service-consolidation
|
||||
tags:
|
||||
- architecture
|
||||
- mcp
|
||||
- cloud
|
||||
- performance
|
||||
- deployment
|
||||
status: in-progress
|
||||
---
|
||||
|
||||
## Status Update
|
||||
|
||||
**Phase 0 (Basic Memory Refactor): ✅ COMPLETE**
|
||||
- basic-memory PR #344: async_client context manager pattern implemented
|
||||
- All 17 MCP tools updated to use `async with get_client() as client:`
|
||||
- CLI commands updated to use context manager
|
||||
- Removed `inject_auth_header()` and `headers.py` (~100 lines deleted)
|
||||
- Factory pattern enables clean dependency injection
|
||||
- Tests passing, typecheck clean
|
||||
|
||||
**Phase 0 Integration: ✅ COMPLETE**
|
||||
- basic-memory-cloud updated to use async-client-context-manager branch
|
||||
- Implemented `tenant_direct_client_factory()` with proper context manager pattern
|
||||
- Removed module-level client override hacks
|
||||
- Removed unnecessary `/proxy` prefix stripping (tools pass relative URLs)
|
||||
- Typecheck and lint passing with proper noqa hints
|
||||
- MCP tools confirmed working via inspector (local testing)
|
||||
|
||||
**Phase 1 (Code Consolidation): ✅ COMPLETE**
|
||||
- MCP server mounted on Cloud FastAPI app at /mcp endpoint
|
||||
- AuthKitProvider configured with WorkOS settings
|
||||
- Combined lifespans (Cloud + MCP) working correctly
|
||||
- JWT context middleware integrated
|
||||
- All routes and MCP tools functional
|
||||
|
||||
**Phase 2 (Direct Tenant Transport): ✅ COMPLETE**
|
||||
- TenantDirectTransport implemented with custom httpx transport
|
||||
- Per-request JWT extraction via FastMCP DI
|
||||
- Tenant lookup and signed header generation working
|
||||
- Direct routing to tenant APIs (eliminating HTTP hop)
|
||||
- Transport tests passing (11/11)
|
||||
|
||||
**Phase 3 (Testing & Validation): ✅ COMPLETE**
|
||||
- Typecheck and lint passing across all services
|
||||
- MCP OAuth authentication working in preview environment
|
||||
- Tenant isolation via signed headers verified
|
||||
- Fixed BM_TENANT_HEADER_SECRET mismatch between environments
|
||||
- MCP tools successfully calling tenant APIs in preview
|
||||
|
||||
**Phase 4 (Deployment Configuration): ✅ COMPLETE**
|
||||
- Updated apps/cloud/fly.template.toml with MCP environment variables
|
||||
- Added HTTP/2 backend support for better MCP performance
|
||||
- Added OAuth protected resource health check
|
||||
- Removed MCP from preview deployment workflow
|
||||
- Successfully deployed to preview environment (PR #113)
|
||||
- All services operational at pr-113-basic-memory-cloud.fly.dev
|
||||
|
||||
**Next Steps:**
|
||||
- Phase 5: Cleanup (remove apps/mcp directory)
|
||||
- Phase 6: Production rollout and performance measurement
|
||||
|
||||
# SPEC-16: MCP Cloud Service Consolidation
|
||||
|
||||
## Why
|
||||
|
||||
### Original Architecture Constraints (Now Removed)
|
||||
|
||||
The current architecture deploys MCP Gateway and Cloud Service as separate Fly.io apps:
|
||||
|
||||
**Current Flow:**
|
||||
```
|
||||
LLM Client → MCP Gateway (OAuth) → Cloud Proxy (JWT + header signing) → Tenant API (JWT + header validation)
|
||||
apps/mcp apps/cloud /proxy apps/api
|
||||
```
|
||||
|
||||
This separation was originally necessary because:
|
||||
1. **Stateful SSE requirement** - MCP needed server-sent events with session state for active project tracking
|
||||
2. **fastmcp.run limitation** - The FastMCP demo helper didn't support worker processes
|
||||
|
||||
### Why These Constraints No Longer Apply
|
||||
|
||||
1. **State externalized** - Project state moved from in-memory to LLM context (external state)
|
||||
2. **HTTP transport enabled** - Switched from SSE to stateless HTTP for MCP tools
|
||||
3. **Worker support added** - Converted from `fastmcp.run()` to `uvicorn.run()` with workers
|
||||
|
||||
### Current Problems
|
||||
|
||||
- **Unnecessary HTTP hop** - MCP tools call Cloud /proxy endpoint which calls tenant API
|
||||
- **Higher latency** - Extra network round trip for every MCP operation
|
||||
- **Increased costs** - Two separate Fly.io apps instead of one
|
||||
- **Complex deployment** - Two services to deploy, monitor, and maintain
|
||||
- **Resource waste** - Separate database connections, HTTP clients, telemetry overhead
|
||||
|
||||
## What
|
||||
|
||||
### Services Affected
|
||||
|
||||
1. **apps/mcp** - MCP Gateway service (to be merged)
|
||||
2. **apps/cloud** - Cloud service (will receive MCP functionality)
|
||||
3. **basic-memory** - Update `async_client.py` to use direct calls
|
||||
4. **Deployment** - Consolidate Fly.io deployment to single app
|
||||
|
||||
### Components Changed
|
||||
|
||||
**Merged:**
|
||||
- MCP middleware and telemetry into Cloud app
|
||||
- MCP tools mounted on Cloud FastAPI instance
|
||||
- ProxyService used directly by MCP tools (not via HTTP)
|
||||
|
||||
**Kept:**
|
||||
- `/proxy` endpoint (still needed by web UI)
|
||||
- All existing Cloud routes (provisioning, webhooks, etc.)
|
||||
- Dual validation in tenant API (JWT + signed headers)
|
||||
|
||||
**Removed:**
|
||||
- apps/mcp directory
|
||||
- Separate MCP Fly.io deployment
|
||||
- HTTP calls from MCP tools to /proxy endpoint
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### 1. Mount FastMCP on Cloud FastAPI App
|
||||
|
||||
```python
|
||||
# apps/cloud/src/basic_memory_cloud/main.py
|
||||
|
||||
from basic_memory.mcp.server import mcp
|
||||
from basic_memory_cloud_mcp.middleware import TelemetryMiddleware
|
||||
|
||||
# Configure MCP OAuth
|
||||
auth_provider = AuthKitProvider(
|
||||
authkit_domain=settings.authkit_domain,
|
||||
base_url=settings.authkit_base_url,
|
||||
required_scopes=[],
|
||||
)
|
||||
mcp.auth = auth_provider
|
||||
mcp.add_middleware(TelemetryMiddleware())
|
||||
|
||||
# Mount MCP at /mcp endpoint
|
||||
mcp_app = mcp.http_app(path="/mcp", stateless_http=True)
|
||||
app.mount("/mcp", mcp_app)
|
||||
|
||||
# Existing Cloud routes stay at root
|
||||
app.include_router(proxy_router)
|
||||
app.include_router(provisioning_router)
|
||||
# ... etc
|
||||
```
|
||||
|
||||
### 2. Direct Tenant Transport (No HTTP Hop)
|
||||
|
||||
Instead of calling `/proxy`, MCP tools call tenant APIs directly via custom httpx transport.
|
||||
|
||||
**Important:** No URL prefix stripping needed. The transport receives relative URLs like `/main/resource/notes/my-note` which are correctly routed to tenant APIs. The `/proxy` prefix only exists for web UI requests to the proxy router, not for MCP tools using the custom transport.
|
||||
|
||||
```python
|
||||
# apps/cloud/src/basic_memory_cloud/transports/tenant_direct.py
|
||||
|
||||
from httpx import AsyncBaseTransport, Request, Response
|
||||
from fastmcp.server.dependencies import get_http_headers
|
||||
import jwt
|
||||
|
||||
class TenantDirectTransport(AsyncBaseTransport):
|
||||
"""Direct transport to tenant APIs, bypassing /proxy endpoint."""
|
||||
|
||||
async def handle_async_request(self, request: Request) -> Response:
|
||||
# 1. Get JWT from current MCP request (via FastMCP DI)
|
||||
http_headers = get_http_headers()
|
||||
auth_header = http_headers.get("authorization") or http_headers.get("Authorization")
|
||||
token = auth_header.replace("Bearer ", "")
|
||||
claims = jwt.decode(token, options={"verify_signature": False})
|
||||
workos_user_id = claims["sub"]
|
||||
|
||||
# 2. Look up tenant for user
|
||||
tenant = await tenant_service.get_tenant_by_user_id(workos_user_id)
|
||||
|
||||
# 3. Build tenant app URL with signed headers
|
||||
fly_app_name = f"{settings.tenant_prefix}-{tenant.id}"
|
||||
target_url = f"https://{fly_app_name}.fly.dev{request.url.path}"
|
||||
|
||||
headers = dict(request.headers)
|
||||
signer = create_signer(settings.bm_tenant_header_secret)
|
||||
headers.update(signer.sign_tenant_headers(tenant.id))
|
||||
|
||||
# 4. Make direct call to tenant API
|
||||
response = await self.client.request(
|
||||
method=request.method, url=target_url,
|
||||
headers=headers, content=request.content
|
||||
)
|
||||
return response
|
||||
```
|
||||
|
||||
Then configure basic-memory's client factory before mounting MCP:
|
||||
|
||||
```python
|
||||
# apps/cloud/src/basic_memory_cloud/main.py
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
from basic_memory.mcp import async_client
|
||||
from basic_memory_cloud.transports.tenant_direct import TenantDirectTransport
|
||||
|
||||
# Configure factory for basic-memory's async_client
|
||||
@asynccontextmanager
|
||||
async def tenant_direct_client_factory():
|
||||
"""Factory for creating clients with tenant direct transport."""
|
||||
client = httpx.AsyncClient(
|
||||
transport=TenantDirectTransport(),
|
||||
base_url="http://direct",
|
||||
)
|
||||
try:
|
||||
yield client
|
||||
finally:
|
||||
await client.aclose()
|
||||
|
||||
# Set factory BEFORE importing MCP tools
|
||||
async_client.set_client_factory(tenant_direct_client_factory)
|
||||
|
||||
# NOW import - tools will use our factory
|
||||
import basic_memory.mcp.tools
|
||||
import basic_memory.mcp.prompts
|
||||
from basic_memory.mcp.server import mcp
|
||||
|
||||
# Mount MCP - tools use direct transport via factory
|
||||
app.mount("/mcp", mcp_app)
|
||||
```
|
||||
|
||||
**Key benefits:**
|
||||
- Clean dependency injection via factory pattern
|
||||
- Per-request tenant resolution via FastMCP DI
|
||||
- Proper resource cleanup (client.aclose() guaranteed)
|
||||
- Eliminates HTTP hop entirely
|
||||
- /proxy endpoint remains for web UI
|
||||
|
||||
### 3. Keep /proxy Endpoint for Web UI
|
||||
|
||||
The existing `/proxy` HTTP endpoint remains functional for:
|
||||
- Web UI requests
|
||||
- Future external API consumers
|
||||
- Backward compatibility
|
||||
|
||||
### 4. Security: Maintain Dual Validation
|
||||
|
||||
**Do NOT remove JWT validation from tenant API.** Keep defense in depth:
|
||||
|
||||
```python
|
||||
# apps/api - Keep both validations
|
||||
1. JWT validation (from WorkOS token)
|
||||
2. Signed header validation (from Cloud/MCP)
|
||||
```
|
||||
|
||||
This ensures if the Cloud service is compromised, attackers still cannot access tenant APIs without valid JWTs.
|
||||
|
||||
### 5. Deployment Changes
|
||||
|
||||
**Before:**
|
||||
- `apps/mcp/fly.template.toml` → MCP Gateway deployment
|
||||
- `apps/cloud/fly.template.toml` → Cloud Service deployment
|
||||
|
||||
**After:**
|
||||
- Remove `apps/mcp/fly.template.toml`
|
||||
- Update `apps/cloud/fly.template.toml` to expose port 8000 for both /mcp and /proxy
|
||||
- Update deployment scripts to deploy single consolidated app
|
||||
|
||||
|
||||
## Basic Memory Dependency: Async Client Refactor
|
||||
|
||||
### Problem
|
||||
The current `basic_memory.mcp.async_client` creates a module-level `client` at import time:
|
||||
```python
|
||||
client = create_client() # Runs immediately when module is imported
|
||||
```
|
||||
|
||||
This prevents dependency injection - by the time we can override it, tools have already imported it.
|
||||
|
||||
### Solution: Context Manager Pattern with Auth at Client Creation
|
||||
|
||||
Refactor basic-memory to use httpx's context manager pattern instead of module-level client.
|
||||
|
||||
**Key principle:** Authentication happens at client creation time, not per-request.
|
||||
|
||||
```python
|
||||
# basic_memory/src/basic_memory/mcp/async_client.py
|
||||
from contextlib import asynccontextmanager
|
||||
from httpx import AsyncClient, ASGITransport, Timeout
|
||||
|
||||
# Optional factory override for dependency injection
|
||||
_client_factory = None
|
||||
|
||||
def set_client_factory(factory):
|
||||
"""Override the default client factory (for cloud app, testing, etc)."""
|
||||
global _client_factory
|
||||
_client_factory = factory
|
||||
|
||||
@asynccontextmanager
|
||||
async def get_client():
|
||||
"""Get an AsyncClient as a context manager.
|
||||
|
||||
Usage:
|
||||
async with get_client() as client:
|
||||
response = await client.get(...)
|
||||
"""
|
||||
if _client_factory:
|
||||
# Cloud app: custom transport handles everything
|
||||
async with _client_factory() as client:
|
||||
yield client
|
||||
else:
|
||||
# Default: create based on config
|
||||
config = ConfigManager().config
|
||||
timeout = Timeout(connect=10.0, read=30.0, write=30.0, pool=30.0)
|
||||
|
||||
if config.cloud_mode_enabled:
|
||||
# CLI cloud mode: inject auth when creating client
|
||||
from basic_memory.cli.auth import CLIAuth
|
||||
|
||||
auth = CLIAuth(
|
||||
client_id=config.cloud_client_id,
|
||||
authkit_domain=config.cloud_domain
|
||||
)
|
||||
token = await auth.get_valid_token()
|
||||
|
||||
if not token:
|
||||
raise RuntimeError(
|
||||
"Cloud mode enabled but not authenticated. "
|
||||
"Run 'basic-memory cloud login' first."
|
||||
)
|
||||
|
||||
# Auth header set ONCE at client creation
|
||||
async with AsyncClient(
|
||||
base_url=f"{config.cloud_host}/proxy",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=timeout
|
||||
) as client:
|
||||
yield client
|
||||
else:
|
||||
# Local mode: ASGI transport
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=fastapi_app),
|
||||
base_url="http://test",
|
||||
timeout=timeout
|
||||
) as client:
|
||||
yield client
|
||||
```
|
||||
|
||||
**Tool Updates:**
|
||||
```python
|
||||
# Before: from basic_memory.mcp.async_client import client
|
||||
from basic_memory.mcp.async_client import get_client
|
||||
|
||||
async def read_note(...):
|
||||
# Before: response = await call_get(client, path, ...)
|
||||
async with get_client() as client:
|
||||
response = await call_get(client, path, ...)
|
||||
# ... use response
|
||||
```
|
||||
|
||||
**Cloud Usage:**
|
||||
```python
|
||||
from contextlib import asynccontextmanager
|
||||
from basic_memory.mcp import async_client
|
||||
|
||||
@asynccontextmanager
|
||||
async def tenant_direct_client():
|
||||
"""Factory for creating clients with tenant direct transport."""
|
||||
client = httpx.AsyncClient(
|
||||
transport=TenantDirectTransport(),
|
||||
base_url="http://direct",
|
||||
)
|
||||
try:
|
||||
yield client
|
||||
finally:
|
||||
await client.aclose()
|
||||
|
||||
# Before importing MCP tools:
|
||||
async_client.set_client_factory(tenant_direct_client)
|
||||
|
||||
# Now import - tools will use our factory
|
||||
import basic_memory.mcp.tools
|
||||
```
|
||||
|
||||
### Benefits
|
||||
- **No module-level state** - client created only when needed
|
||||
- **Proper cleanup** - context manager ensures `aclose()` is called
|
||||
- **Easy dependency injection** - factory pattern allows custom clients
|
||||
- **httpx best practices** - follows official recommendations
|
||||
- **Works for all modes** - stdio, cloud, testing
|
||||
|
||||
### Architecture Simplification: Auth at Client Creation
|
||||
|
||||
**Key design principle:** Authentication happens when creating the client, not on every request.
|
||||
|
||||
**Three modes, three approaches:**
|
||||
|
||||
1. **Local mode (ASGI)**
|
||||
- No auth needed
|
||||
- Direct in-process calls via ASGITransport
|
||||
|
||||
2. **CLI cloud mode (HTTP)**
|
||||
- Auth token from CLIAuth (stored in ~/.basic-memory/basic-memory-cloud.json)
|
||||
- Injected as default header when creating AsyncClient
|
||||
- Single auth check at client creation time
|
||||
|
||||
3. **Cloud app mode (Custom Transport)**
|
||||
- TenantDirectTransport handles everything
|
||||
- Extracts JWT from FastMCP context per-request
|
||||
- No interaction with inject_auth_header() logic
|
||||
|
||||
**What this removes:**
|
||||
- `src/basic_memory/mcp/tools/headers.py` - entire file deleted
|
||||
- `inject_auth_header()` calls in all request helpers (call_get, call_post, etc.)
|
||||
- Per-request header manipulation complexity
|
||||
- Circular dependency concerns between async_client and auth logic
|
||||
|
||||
**Benefits:**
|
||||
- Cleaner separation of concerns
|
||||
- Simpler request helper functions
|
||||
- Auth happens at the right layer (client creation)
|
||||
- Cloud app transport is completely independent
|
||||
|
||||
### Refactor Summary
|
||||
|
||||
This refactor achieves:
|
||||
|
||||
**Simplification:**
|
||||
- Removes ~100 lines of per-request header injection logic
|
||||
- Deletes entire `headers.py` module
|
||||
- Auth happens once at client creation, not per-request
|
||||
|
||||
**Decoupling:**
|
||||
- Cloud app's custom transport is completely independent
|
||||
- No interaction with basic-memory's auth logic
|
||||
- Each mode (local, CLI cloud, cloud app) has clean separation
|
||||
|
||||
**Better Design:**
|
||||
- Follows httpx best practices (context managers)
|
||||
- Proper resource cleanup (client.aclose() guaranteed)
|
||||
- Easier testing via factory injection
|
||||
- No circular import risks
|
||||
|
||||
**Three Distinct Modes:**
|
||||
1. Local: ASGI transport, no auth
|
||||
2. CLI cloud: HTTP transport with CLIAuth token injection
|
||||
3. Cloud app: Custom transport with per-request tenant routing
|
||||
|
||||
### Implementation Plan Summary
|
||||
1. Create branch `async-client-context-manager` in basic-memory
|
||||
2. Update `async_client.py` with context manager pattern and CLIAuth integration
|
||||
3. Remove `inject_auth_header()` from all request helpers
|
||||
4. Delete `src/basic_memory/mcp/tools/headers.py`
|
||||
5. Update all MCP tools to use `async with get_client() as client:`
|
||||
6. Update CLI commands to use context manager and remove manual auth
|
||||
7. Remove `api_url` config field
|
||||
8. Update tests
|
||||
9. Update basic-memory-cloud to use branch: `basic-memory @ git+https://github.com/basicmachines-co/basic-memory.git@async-client-context-manager`
|
||||
|
||||
Detailed breakdown in Phase 0 tasks below.
|
||||
|
||||
### Implementation Notes
|
||||
|
||||
**Potential Issues & Solutions:**
|
||||
|
||||
1. **Circular Import** (async_client imports CLIAuth)
|
||||
- **Risk:** CLIAuth might import something from async_client
|
||||
- **Solution:** Use lazy import inside `get_client()` function
|
||||
- **Already done:** Import is inside the function, not at module level
|
||||
|
||||
2. **Test Fixtures**
|
||||
- **Risk:** Tests using module-level client will break
|
||||
- **Solution:** Update fixtures to use factory pattern
|
||||
- **Example:**
|
||||
```python
|
||||
@pytest.fixture
|
||||
def mock_client_factory():
|
||||
@asynccontextmanager
|
||||
async def factory():
|
||||
async with AsyncClient(...) as client:
|
||||
yield client
|
||||
return factory
|
||||
```
|
||||
|
||||
3. **Performance**
|
||||
- **Risk:** Creating client per tool call might be expensive
|
||||
- **Reality:** httpx is designed for this pattern, connection pooling at transport level
|
||||
- **Mitigation:** Monitor performance, can optimize later if needed
|
||||
|
||||
4. **CLI Cloud Commands Edge Cases**
|
||||
- **Risk:** Token expires mid-operation
|
||||
- **Solution:** CLIAuth.get_valid_token() already handles refresh
|
||||
- **Validation:** Test cloud login → use tools → token refresh flow
|
||||
|
||||
5. **Backward Compatibility**
|
||||
- **Risk:** External code importing `client` directly
|
||||
- **Solution:** Keep `create_client()` and `client` for one version, deprecate
|
||||
- **Timeline:** Remove in next major version
|
||||
|
||||
## Implementation Tasks
|
||||
|
||||
### Phase 0: Basic Memory Refactor (Prerequisite)
|
||||
|
||||
#### 0.1 Core Refactor - async_client.py
|
||||
- [x] Create branch `async-client-context-manager` in basic-memory repo
|
||||
- [x] Implement `get_client()` context manager
|
||||
- [x] Implement `set_client_factory()` for dependency injection
|
||||
- [x] Add CLI cloud mode auth injection (CLIAuth integration)
|
||||
- [x] Remove `api_url` config field (legacy, unused)
|
||||
- [x] Keep `create_client()` temporarily for backward compatibility (deprecate later)
|
||||
|
||||
#### 0.2 Simplify Request Helpers - tools/utils.py
|
||||
- [x] Remove `inject_auth_header()` calls from `call_get()`
|
||||
- [x] Remove `inject_auth_header()` calls from `call_post()`
|
||||
- [x] Remove `inject_auth_header()` calls from `call_put()`
|
||||
- [x] Remove `inject_auth_header()` calls from `call_patch()`
|
||||
- [x] Remove `inject_auth_header()` calls from `call_delete()`
|
||||
- [x] Delete `src/basic_memory/mcp/tools/headers.py` entirely
|
||||
- [x] Update imports in utils.py
|
||||
|
||||
#### 0.3 Update MCP Tools (~16 files)
|
||||
Convert from `from async_client import client` to `async with get_client() as client:`
|
||||
|
||||
- [x] `tools/write_note.py` (34/34 tests passing)
|
||||
- [x] `tools/read_note.py` (21/21 tests passing)
|
||||
- [x] `tools/view_note.py` (12/12 tests passing - no changes needed, delegates to read_note)
|
||||
- [x] `tools/delete_note.py` (2/2 tests passing)
|
||||
- [x] `tools/read_content.py` (20/20 tests passing)
|
||||
- [x] `tools/list_directory.py` (11/11 tests passing)
|
||||
- [x] `tools/move_note.py` (34/34 tests passing, 90% coverage)
|
||||
- [x] `tools/search.py` (16/16 tests passing, 96% coverage)
|
||||
- [x] `tools/recent_activity.py` (4/4 tests passing, 82% coverage)
|
||||
- [x] `tools/project_management.py` (3 functions: list_memory_projects, create_memory_project, delete_project - typecheck passed)
|
||||
- [x] `tools/edit_note.py` (17/17 tests passing)
|
||||
- [x] `tools/canvas.py` (5/5 tests passing)
|
||||
- [x] `tools/build_context.py` (6/6 tests passing)
|
||||
- [x] `tools/sync_status.py` (typecheck passed)
|
||||
- [x] `prompts/continue_conversation.py` (typecheck passed)
|
||||
- [x] `prompts/search.py` (typecheck passed)
|
||||
- [x] `resources/project_info.py` (typecheck passed)
|
||||
|
||||
#### 0.4 Update CLI Commands (~3 files)
|
||||
Remove manual auth header passing, use context manager:
|
||||
|
||||
- [x] `cli/commands/project.py` - removed get_authenticated_headers() calls, use context manager
|
||||
- [x] `cli/commands/status.py` - use context manager
|
||||
- [x] `cli/commands/command_utils.py` - use context manager
|
||||
|
||||
#### 0.5 Update Config
|
||||
- [x] Remove `api_url` field from `BasicMemoryConfig` in config.py
|
||||
- [x] Update any lingering references/docs (added deprecation notice to v15-docs/cloud-mode-usage.md)
|
||||
|
||||
#### 0.6 Testing
|
||||
- [-] Update test fixtures to use factory pattern
|
||||
- [x] Run full test suite in basic-memory
|
||||
- [x] Verify cloud_mode_enabled works with CLIAuth injection
|
||||
- [x] Run typecheck and linting
|
||||
|
||||
#### 0.7 Cloud Integration Prep
|
||||
- [x] Update basic-memory-cloud pyproject.toml to use branch
|
||||
- [x] Implement factory pattern in cloud app main.py
|
||||
- [x] Remove `/proxy` prefix stripping logic (not needed - tools pass relative URLs)
|
||||
|
||||
#### 0.8 Phase 0 Validation
|
||||
|
||||
**Before merging async-client-context-manager branch:**
|
||||
|
||||
- [x] All tests pass locally
|
||||
- [x] Typecheck passes (pyright/mypy)
|
||||
- [x] Linting passes (ruff)
|
||||
- [x] Manual test: local mode works (ASGI transport)
|
||||
- [x] Manual test: cloud login → cloud mode works (HTTP transport with auth)
|
||||
- [x] No import of `inject_auth_header` anywhere
|
||||
- [x] `headers.py` file deleted
|
||||
- [x] `api_url` config removed
|
||||
- [x] Tool functions properly scoped (client inside async with)
|
||||
- [ ] CLI commands properly scoped (client inside async with)
|
||||
|
||||
**Integration validation:**
|
||||
- [x] basic-memory-cloud can import and use factory pattern
|
||||
- [x] TenantDirectTransport works without touching header injection
|
||||
- [x] No circular imports or lazy import issues
|
||||
- [x] MCP tools work via inspector (local testing confirmed)
|
||||
|
||||
### Phase 1: Code Consolidation
|
||||
- [x] Create feature branch `consolidate-mcp-cloud`
|
||||
- [x] Update `apps/cloud/src/basic_memory_cloud/config.py`:
|
||||
- [x] Add `authkit_base_url` field (already has authkit_domain)
|
||||
- [x] Workers config already exists ✓
|
||||
- [x] Update `apps/cloud/src/basic_memory_cloud/telemetry.py`:
|
||||
- [x] Add `logfire.instrument_mcp()` to existing setup
|
||||
- [x] Skip complex two-phase setup - use Cloud's simpler approach
|
||||
- [x] Create `apps/cloud/src/basic_memory_cloud/middleware/jwt_context.py`:
|
||||
- [x] FastAPI middleware to extract JWT claims from Authorization header
|
||||
- [x] Add tenant context (workos_user_id) to logfire baggage
|
||||
- [x] Simpler than FastMCP middleware version
|
||||
- [x] Update `apps/cloud/src/basic_memory_cloud/main.py`:
|
||||
- [x] Import FastMCP server from basic-memory
|
||||
- [x] Configure AuthKitProvider with WorkOS settings
|
||||
- [x] No FastMCP telemetry middleware needed (using FastAPI middleware instead)
|
||||
- [x] Create MCP ASGI app: `mcp_app = mcp.http_app(path='/mcp', stateless_http=True)`
|
||||
- [x] Combine lifespans (Cloud + MCP) using nested async context managers
|
||||
- [x] Mount MCP: `app.mount("/mcp", mcp_app)`
|
||||
- [x] Add JWT context middleware to FastAPI app
|
||||
- [x] Run typecheck - passes ✓
|
||||
|
||||
### Phase 2: Direct Tenant Transport
|
||||
- [x] Create `apps/cloud/src/basic_memory_cloud/transports/tenant_direct.py`:
|
||||
- [x] Implement `TenantDirectTransport(AsyncBaseTransport)`
|
||||
- [x] Use FastMCP DI (`get_http_headers()`) to extract JWT per-request
|
||||
- [x] Decode JWT to get `workos_user_id`
|
||||
- [x] Look up/create tenant via `TenantRepository.get_or_create_tenant_for_workos_user()`
|
||||
- [x] Build tenant app URL and add signed headers
|
||||
- [x] Make direct httpx call to tenant API
|
||||
- [x] No `/proxy` prefix stripping needed (tools pass relative URLs like `/main/resource/...`)
|
||||
- [x] Update `apps/cloud/src/basic_memory_cloud/main.py`:
|
||||
- [x] Refactored to use factory pattern instead of module-level override
|
||||
- [x] Implement `tenant_direct_client_factory()` context manager
|
||||
- [x] Call `async_client.set_client_factory()` before importing MCP tools
|
||||
- [x] Clean imports, proper noqa hints for lint
|
||||
- [x] Basic-memory refactor integrated (PR #344)
|
||||
- [x] Run typecheck - passes ✓
|
||||
- [x] Run lint - passes ✓
|
||||
|
||||
### Phase 3: Testing & Validation
|
||||
- [x] Run `just typecheck` in apps/cloud
|
||||
- [x] Run `just check` in project
|
||||
- [x] Run `just fix` - all lint errors fixed ✓
|
||||
- [x] Write comprehensive transport tests (11 tests passing) ✓
|
||||
- [x] Test MCP tools locally with consolidated service (inspector confirmed working)
|
||||
- [x] Verify OAuth authentication works (requires full deployment)
|
||||
- [x] Verify tenant isolation via signed headers (requires full deployment)
|
||||
- [x] Test /proxy endpoint still works for web UI
|
||||
- [ ] Measure latency before/after consolidation
|
||||
- [ ] Check telemetry traces span correctly
|
||||
|
||||
### Phase 4: Deployment Configuration
|
||||
- [x] Update `apps/cloud/fly.template.toml`:
|
||||
- [x] Merged MCP-specific environment variables (AUTHKIT_BASE_URL, FASTMCP_LOG_LEVEL, BASIC_MEMORY_*)
|
||||
- [x] Added HTTP/2 backend support (`h2_backend = true`) for better MCP performance
|
||||
- [x] Added health check for MCP OAuth endpoint (`/.well-known/oauth-protected-resource`)
|
||||
- [x] Port 8000 already exposed - serves both Cloud routes and /mcp endpoint
|
||||
- [x] Workers configured (UVICORN_WORKERS = 4)
|
||||
- [x] Update `.env.example`:
|
||||
- [x] Consolidated MCP Gateway section into Cloud app section
|
||||
- [x] Added AUTHKIT_BASE_URL, FASTMCP_LOG_LEVEL, BASIC_MEMORY_HOME
|
||||
- [x] Added LOG_LEVEL to Development Settings
|
||||
- [x] Documented that MCP now served at /mcp on Cloud service (port 8000)
|
||||
- [x] Test deployment to preview environment (PR #113)
|
||||
- [x] OAuth authentication verified
|
||||
- [x] MCP tools successfully calling tenant APIs
|
||||
- [x] Fixed BM_TENANT_HEADER_SECRET synchronization issue
|
||||
|
||||
### Phase 5: Cleanup
|
||||
- [x] Remove `apps/mcp/` directory entirely
|
||||
- [x] Remove MCP-specific fly.toml and deployment configs
|
||||
- [x] Update repository documentation
|
||||
- [x] Update CLAUDE.md with new architecture
|
||||
- [-] Archive old MCP deployment configs (if needed)
|
||||
|
||||
### Phase 6: Production Rollout
|
||||
- [ ] Deploy to development and validate
|
||||
- [ ] Monitor metrics and logs
|
||||
- [ ] Deploy to production
|
||||
- [ ] Verify production functionality
|
||||
- [ ] Document performance improvements
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1: Preparation
|
||||
1. Create feature branch `consolidate-mcp-cloud`
|
||||
2. Update basic-memory async_client.py for direct ProxyService calls
|
||||
3. Update apps/cloud/main.py to mount MCP
|
||||
|
||||
### Phase 2: Testing
|
||||
1. Local testing with consolidated app
|
||||
2. Deploy to development environment
|
||||
3. Run full test suite
|
||||
4. Performance benchmarking
|
||||
|
||||
### Phase 3: Deployment
|
||||
1. Deploy to development
|
||||
2. Validate all functionality
|
||||
3. Deploy to production
|
||||
4. Monitor for issues
|
||||
|
||||
### Phase 4: Cleanup
|
||||
1. Remove apps/mcp directory
|
||||
2. Update documentation
|
||||
3. Update deployment scripts
|
||||
4. Archive old MCP deployment configs
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues arise:
|
||||
1. Revert feature branch
|
||||
2. Redeploy separate apps/mcp and apps/cloud services
|
||||
3. Restore previous fly.toml configurations
|
||||
4. Document issues encountered
|
||||
|
||||
The well-organized code structure makes splitting back out feasible if future scaling needs diverge.
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### 1. Functional Testing
|
||||
|
||||
**MCP Tools:**
|
||||
- [ ] All 17 MCP tools work via consolidated /mcp endpoint
|
||||
- [x] OAuth authentication validates correctly
|
||||
- [x] Tenant isolation maintained via signed headers
|
||||
- [x] Project management tools function correctly
|
||||
|
||||
**Cloud Routes:**
|
||||
- [x] /proxy endpoint still works for web UI
|
||||
- [x] /provisioning routes functional
|
||||
- [x] /webhooks routes functional
|
||||
- [x] /tenants routes functional
|
||||
|
||||
**API Validation:**
|
||||
- [x] Tenant API validates both JWT and signed headers
|
||||
- [x] Unauthorized requests rejected appropriately
|
||||
- [x] Multi-tenant isolation verified
|
||||
|
||||
### 2. Performance Testing
|
||||
|
||||
**Latency Reduction:**
|
||||
- [x] Measure MCP tool latency before consolidation
|
||||
- [x] Measure MCP tool latency after consolidation
|
||||
- [x] Verify reduction from eliminated HTTP hop (expected: 20-50ms improvement)
|
||||
|
||||
**Resource Usage:**
|
||||
- [x] Single app uses less total memory than two apps
|
||||
- [x] Database connection pooling more efficient
|
||||
- [x] HTTP client overhead reduced
|
||||
|
||||
### 3. Deployment Testing
|
||||
|
||||
**Fly.io Deployment:**
|
||||
- [x] Single app deploys successfully
|
||||
- [x] Health checks pass for consolidated service
|
||||
- [x] No apps/mcp deployment required
|
||||
- [x] Environment variables configured correctly
|
||||
|
||||
**Local Development:**
|
||||
- [x] `just setup` works with consolidated architecture
|
||||
- [x] Local testing shows MCP tools working
|
||||
- [x] No regression in developer experience
|
||||
|
||||
### 4. Security Validation
|
||||
|
||||
**Defense in Depth:**
|
||||
- [x] Tenant API still validates JWT tokens
|
||||
- [x] Tenant API still validates signed headers
|
||||
- [x] No access possible with only signed headers (JWT required)
|
||||
- [x] No access possible with only JWT (signed headers required)
|
||||
|
||||
**Authorization:**
|
||||
- [x] Users can only access their own tenant data
|
||||
- [x] Cross-tenant requests rejected
|
||||
- [x] Admin operations require proper authentication
|
||||
|
||||
### 5. Observability
|
||||
|
||||
**Telemetry:**
|
||||
- [x] OpenTelemetry traces span across MCP → ProxyService → Tenant API
|
||||
- [x] Logfire shows consolidated traces correctly
|
||||
- [x] Error tracking and debugging still functional
|
||||
- [x] Performance metrics accurate
|
||||
|
||||
**Logging:**
|
||||
- [x] Structured logs show proper context (tenant_id, operation, etc.)
|
||||
- [x] Error logs contain actionable information
|
||||
- [x] Log volume reasonable for single app
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. **Functionality**: All MCP tools and Cloud routes work identically to before
|
||||
2. **Performance**: Measurable latency reduction (>20ms average)
|
||||
3. **Cost**: Single Fly.io app instead of two (50% infrastructure reduction)
|
||||
4. **Security**: Dual validation maintained, no security regression
|
||||
5. **Deployment**: Simplified deployment process, single app to manage
|
||||
6. **Observability**: Telemetry and logging work correctly
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
### Future Considerations
|
||||
|
||||
- **Independent scaling**: If MCP and Cloud need different scaling profiles in future, code organization supports splitting back out
|
||||
- **Regional deployment**: Consolidated app can still be deployed to multiple regions
|
||||
- **Edge caching**: Could add edge caching layer in front of consolidated service
|
||||
|
||||
### Dependencies
|
||||
|
||||
- SPEC-9: Signed Header Tenant Information (already implemented)
|
||||
- SPEC-12: OpenTelemetry Observability (telemetry must work across merged services)
|
||||
|
||||
### Related Work
|
||||
|
||||
- basic-memory v0.13.x: MCP server implementation
|
||||
- FastMCP documentation: Mounting on existing FastAPI apps
|
||||
- Fly.io multi-service patterns
|
||||
@@ -0,0 +1,528 @@
|
||||
---
|
||||
title: 'SPEC-18: AI Memory Management Tool'
|
||||
type: spec
|
||||
permalink: specs/spec-15-ai-memory-management-tool
|
||||
tags:
|
||||
- mcp
|
||||
- memory
|
||||
- ai-context
|
||||
- tools
|
||||
---
|
||||
|
||||
# SPEC-18: AI Memory Management Tool
|
||||
|
||||
## Why
|
||||
|
||||
Anthropic recently released a memory tool for Claude that enables storing and retrieving information across conversations using client-side file operations. This validates Basic Memory's local-first, file-based architecture - Anthropic converged on the same pattern.
|
||||
|
||||
However, Anthropic's memory tool is only available via their API and stores plain text. Basic Memory can offer a superior implementation through MCP that:
|
||||
|
||||
1. **Works everywhere** - Claude Desktop, Code, VS Code, Cursor via MCP (not just API)
|
||||
2. **Structured knowledge** - Entities with observations/relations vs plain text
|
||||
3. **Full search** - Full-text search, graph traversal, time-aware queries
|
||||
4. **Unified storage** - Agent memories + user notes in one knowledge graph
|
||||
5. **Existing infrastructure** - Leverages SQLite indexing, sync, multi-project support
|
||||
|
||||
This would enable AI agents to store contextual memories alongside user notes, with all the power of Basic Memory's knowledge graph features.
|
||||
|
||||
## What
|
||||
|
||||
Create a new MCP tool `memory` that matches Anthropic's tool interface exactly, allowing Claude to use it with zero learning curve. The tool will store files in Basic Memory's `/memories` directory and support Basic Memory's structured markdown format in the file content.
|
||||
|
||||
### Affected Components
|
||||
|
||||
- **New MCP Tool**: `src/basic_memory/mcp/tools/memory_tool.py`
|
||||
- **Dedicated Memories Project**: Create a separate "memories" Basic Memory project
|
||||
- **Project Isolation**: Memories stored separately from user notes/documents
|
||||
- **File Organization**: Within the memories project, use folder structure:
|
||||
- `user/` - User preferences, context, communication style
|
||||
- `projects/` - Project-specific state and decisions
|
||||
- `sessions/` - Conversation-specific working memory
|
||||
- `patterns/` - Learned patterns and insights
|
||||
|
||||
### Tool Commands
|
||||
|
||||
The tool will support these commands (exactly matching Anthropic's interface):
|
||||
|
||||
- `view` - Display directory contents or file content (with optional line range)
|
||||
- `create` - Create or overwrite a file with given content
|
||||
- `str_replace` - Replace text in an existing file
|
||||
- `insert` - Insert text at specific line number
|
||||
- `delete` - Delete file or directory
|
||||
- `rename` - Move or rename file/directory
|
||||
|
||||
### Memory Note Format
|
||||
|
||||
Memories will use Basic Memory's standard structure:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: User Preferences
|
||||
permalink: memories/user/preferences
|
||||
type: memory
|
||||
memory_type: preferences
|
||||
created_by: claude
|
||||
tags: [user, preferences, style]
|
||||
---
|
||||
|
||||
# User Preferences
|
||||
|
||||
## Observations
|
||||
- [communication] Prefers concise, direct responses without preamble #style
|
||||
- [tone] Appreciates validation but dislikes excessive apologizing #communication
|
||||
- [technical] Works primarily in Python with type annotations #coding
|
||||
|
||||
## Relations
|
||||
- relates_to [[Basic Memory Project]]
|
||||
- informs [[Response Style Guidelines]]
|
||||
```
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### Implementation Approach
|
||||
|
||||
The memory tool matches Anthropic's interface but uses a dedicated Basic Memory project:
|
||||
|
||||
```python
|
||||
async def memory_tool(
|
||||
command: str,
|
||||
path: str,
|
||||
file_text: Optional[str] = None,
|
||||
old_str: Optional[str] = None,
|
||||
new_str: Optional[str] = None,
|
||||
insert_line: Optional[int] = None,
|
||||
insert_text: Optional[str] = None,
|
||||
old_path: Optional[str] = None,
|
||||
new_path: Optional[str] = None,
|
||||
view_range: Optional[List[int]] = None,
|
||||
):
|
||||
"""Memory tool with Anthropic-compatible interface.
|
||||
|
||||
Operates on a dedicated "memories" Basic Memory project,
|
||||
keeping AI memories separate from user notes.
|
||||
"""
|
||||
|
||||
# Get the memories project (auto-created if doesn't exist)
|
||||
memories_project = get_or_create_memories_project()
|
||||
|
||||
# Validate path security using pathlib (prevent directory traversal)
|
||||
safe_path = validate_memory_path(path, memories_project.project_path)
|
||||
|
||||
# Use existing project isolation - already prevents cross-project access
|
||||
full_path = memories_project.project_path / safe_path
|
||||
|
||||
if command == "view":
|
||||
# Return directory listing or file content
|
||||
if full_path.is_dir():
|
||||
return list_directory_contents(full_path)
|
||||
return read_file_content(full_path, view_range)
|
||||
|
||||
elif command == "create":
|
||||
# Write file directly (file_text can contain BM markdown)
|
||||
full_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
full_path.write_text(file_text)
|
||||
# Sync service will detect and index automatically
|
||||
return f"Created {path}"
|
||||
|
||||
elif command == "str_replace":
|
||||
# Read, replace, write
|
||||
content = full_path.read_text()
|
||||
updated = content.replace(old_str, new_str)
|
||||
full_path.write_text(updated)
|
||||
return f"Replaced text in {path}"
|
||||
|
||||
elif command == "insert":
|
||||
# Insert at line number
|
||||
lines = full_path.read_text().splitlines()
|
||||
lines.insert(insert_line, insert_text)
|
||||
full_path.write_text("\n".join(lines))
|
||||
return f"Inserted text at line {insert_line}"
|
||||
|
||||
elif command == "delete":
|
||||
# Delete file or directory
|
||||
if full_path.is_dir():
|
||||
shutil.rmtree(full_path)
|
||||
else:
|
||||
full_path.unlink()
|
||||
return f"Deleted {path}"
|
||||
|
||||
elif command == "rename":
|
||||
# Move/rename
|
||||
full_path.rename(config.project_path / new_path)
|
||||
return f"Renamed {old_path} to {new_path}"
|
||||
```
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
1. **Exact interface match** - Same commands, parameters as Anthropic's tool
|
||||
2. **Dedicated memories project** - Separate Basic Memory project keeps AI memories isolated from user notes
|
||||
3. **Existing project isolation** - Leverage BM's existing cross-project security (no additional validation needed)
|
||||
4. **Direct file I/O** - No schema conversion, just read/write files
|
||||
5. **Structured content supported** - `file_text` can use BM markdown format with frontmatter, observations, relations
|
||||
6. **Automatic indexing** - Sync service watches memories project and indexes changes
|
||||
7. **Path security** - Use `pathlib.Path.resolve()` and `relative_to()` to prevent directory traversal
|
||||
8. **Error handling** - Follow Anthropic's text editor tool error patterns
|
||||
|
||||
### MCP Tool Schema
|
||||
|
||||
Exact match to Anthropic's memory tool schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "memory",
|
||||
"description": "Store and retrieve information across conversations using structured markdown files. All operations must be within the /memories directory. Supports Basic Memory markdown format including frontmatter, observations, and relations.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {
|
||||
"type": "string",
|
||||
"enum": ["view", "create", "str_replace", "insert", "delete", "rename"],
|
||||
"description": "File operation to perform"
|
||||
},
|
||||
"path": {shu
|
||||
"type": "string",
|
||||
"description": "Path within /memories directory (required for all commands)"
|
||||
},
|
||||
"file_text": {
|
||||
"type": "string",
|
||||
"description": "Content to write (for create command). Supports Basic Memory markdown format."
|
||||
},
|
||||
"view_range": {
|
||||
"type": "array",
|
||||
"items": {"type": "integer"},
|
||||
"description": "Optional [start, end] line range for view command"
|
||||
},
|
||||
"old_str": {
|
||||
"type": "string",
|
||||
"description": "Text to replace (for str_replace command)"
|
||||
},
|
||||
"new_str": {
|
||||
"type": "string",
|
||||
"description": "Replacement text (for str_replace command)"
|
||||
},
|
||||
"insert_line": {
|
||||
"type": "integer",
|
||||
"description": "Line number to insert at (for insert command)"
|
||||
},
|
||||
"insert_text": {
|
||||
"type": "string",
|
||||
"description": "Text to insert (for insert command)"
|
||||
},
|
||||
"old_path": {
|
||||
"type": "string",
|
||||
"description": "Current path (for rename command)"
|
||||
},
|
||||
"new_path": {
|
||||
"type": "string",
|
||||
"description": "New path (for rename command)"
|
||||
}
|
||||
},
|
||||
"required": ["command", "path"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prompting Guidance
|
||||
|
||||
When the `memory` tool is included, Basic Memory should provide system prompt guidance to help Claude use it effectively.
|
||||
|
||||
#### Automatic System Prompt Addition
|
||||
|
||||
```text
|
||||
MEMORY PROTOCOL FOR BASIC MEMORY:
|
||||
1. ALWAYS check your memory directory first using `view` command on root directory
|
||||
2. Your memories are stored in a dedicated Basic Memory project (isolated from user notes)
|
||||
3. Use structured markdown format in memory files:
|
||||
- Include frontmatter with title, type: memory, tags
|
||||
- Use ## Observations with [category] prefixes for facts
|
||||
- Use ## Relations to link memories with [[WikiLinks]]
|
||||
4. Record progress, context, and decisions as categorized observations
|
||||
5. Link related memories using relations
|
||||
6. ASSUME INTERRUPTION: Context may reset - save progress frequently
|
||||
|
||||
MEMORY ORGANIZATION:
|
||||
- user/ - User preferences, context, communication style
|
||||
- projects/ - Project-specific state and decisions
|
||||
- sessions/ - Conversation-specific working memory
|
||||
- patterns/ - Learned patterns and insights
|
||||
|
||||
MEMORY ADVANTAGES:
|
||||
- Your memories are automatically searchable via full-text search
|
||||
- Relations create a knowledge graph you can traverse
|
||||
- Memories are isolated from user notes (separate project)
|
||||
- Use search_notes(project="memories") to find relevant past context
|
||||
- Use recent_activity(project="memories") to see what changed recently
|
||||
- Use build_context() to navigate memory relations
|
||||
```
|
||||
|
||||
#### Optional MCP Prompt: `memory_guide`
|
||||
|
||||
Create an MCP prompt that provides detailed guidance and examples:
|
||||
|
||||
```python
|
||||
{
|
||||
"name": "memory_guide",
|
||||
"description": "Comprehensive guidance for using Basic Memory's memory tool effectively, including structured markdown examples and best practices"
|
||||
}
|
||||
```
|
||||
|
||||
This prompt returns:
|
||||
- Full protocol and conventions
|
||||
- Example memory file structures
|
||||
- Tips for organizing observations and relations
|
||||
- Integration with other Basic Memory tools
|
||||
- Common patterns (user preferences, project state, session tracking)
|
||||
|
||||
#### User Customization
|
||||
|
||||
Users can customize memory behavior with additional instructions:
|
||||
- "Only write information relevant to [topic] in your memory system"
|
||||
- "Keep memory files concise and organized - delete outdated content"
|
||||
- "Use detailed observations for technical decisions and implementation notes"
|
||||
- "Always link memories to related project documentation using relations"
|
||||
|
||||
### Error Handling
|
||||
|
||||
Follow Anthropic's text editor tool error handling patterns for consistency:
|
||||
|
||||
#### Error Types
|
||||
|
||||
1. **File Not Found**
|
||||
```json
|
||||
{"error": "File not found: memories/user/preferences.md", "is_error": true}
|
||||
```
|
||||
|
||||
2. **Permission Denied**
|
||||
```json
|
||||
{"error": "Permission denied: Cannot write outside /memories directory", "is_error": true}
|
||||
```
|
||||
|
||||
3. **Invalid Path (Directory Traversal)**
|
||||
```json
|
||||
{"error": "Invalid path: Path must be within /memories directory", "is_error": true}
|
||||
```
|
||||
|
||||
4. **Multiple Matches (str_replace)**
|
||||
```json
|
||||
{"error": "Found 3 matches for replacement text. Please provide more context to make a unique match.", "is_error": true}
|
||||
```
|
||||
|
||||
5. **No Matches (str_replace)**
|
||||
```json
|
||||
{"error": "No match found for replacement. Please check your text and try again.", "is_error": true}
|
||||
```
|
||||
|
||||
6. **Invalid Line Number (insert)**
|
||||
```json
|
||||
{"error": "Invalid line number: File has 20 lines, cannot insert at line 100", "is_error": true}
|
||||
```
|
||||
|
||||
#### Error Handling Best Practices
|
||||
|
||||
- **Path validation** - Use `pathlib.Path.resolve()` and `relative_to()` to validate paths
|
||||
```python
|
||||
def validate_memory_path(path: str, project_path: Path) -> Path:
|
||||
"""Validate path is within memories project directory."""
|
||||
# Resolve to canonical form
|
||||
full_path = (project_path / path).resolve()
|
||||
|
||||
# Ensure it's relative to project path (prevents directory traversal)
|
||||
try:
|
||||
full_path.relative_to(project_path)
|
||||
return full_path
|
||||
except ValueError:
|
||||
raise ValueError("Invalid path: Path must be within memories project")
|
||||
```
|
||||
- **Project isolation** - Leverage existing Basic Memory project isolation (prevents cross-project access)
|
||||
- **File existence** - Verify file exists before read/modify operations
|
||||
- **Clear messages** - Provide specific, actionable error messages
|
||||
- **Structured responses** - Always include `is_error: true` flag in error responses
|
||||
- **Security checks** - Reject `../`, `..\\`, URL-encoded sequences (`%2e%2e%2f`)
|
||||
- **Match validation** - For `str_replace`, ensure exactly one match or return helpful error
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
|
||||
1. **Functional completeness**:
|
||||
- All 6 commands work (view, create, str_replace, insert, delete, rename)
|
||||
- Dedicated "memories" Basic Memory project auto-created on first use
|
||||
- Files stored within memories project (isolated from user notes)
|
||||
- Path validation uses `pathlib` to prevent directory traversal
|
||||
- Commands match Anthropic's exact interface
|
||||
|
||||
2. **Integration with existing features**:
|
||||
- Memories project uses existing BM project isolation
|
||||
- Sync service detects file changes in memories project
|
||||
- Created files get indexed automatically by sync service
|
||||
- `search_notes(project="memories")` finds memory files
|
||||
- `build_context()` can traverse relations in memory files
|
||||
- `recent_activity(project="memories")` surfaces recent memory changes
|
||||
|
||||
3. **Test coverage**:
|
||||
- Unit tests for all 6 memory tool commands
|
||||
- Test memories project auto-creation on first use
|
||||
- Test project isolation (cannot access files outside memories project)
|
||||
- Test sync service watching memories project
|
||||
- Test that memory files with BM markdown get indexed correctly
|
||||
- Test path validation using `pathlib` (rejects `../`, absolute paths, etc.)
|
||||
- Test memory search, relations, and graph traversal within memories project
|
||||
- Test all error conditions (file not found, permission denied, invalid paths, etc.)
|
||||
- Test `str_replace` with no matches, single match, multiple matches
|
||||
- Test `insert` with invalid line numbers
|
||||
|
||||
4. **Prompting system**:
|
||||
- Automatic system prompt addition when `memory` tool is enabled
|
||||
- `memory_guide` MCP prompt provides detailed guidance
|
||||
- Prompts explain BM structured markdown format
|
||||
- Integration with search_notes, build_context, recent_activity
|
||||
|
||||
5. **Documentation**:
|
||||
- Update MCP tools reference with `memory` tool
|
||||
- Add examples showing BM markdown in memory files
|
||||
- Document `/memories` folder structure conventions
|
||||
- Explain advantages over Anthropic's API-only tool
|
||||
- Document prompting guidance and customization
|
||||
|
||||
### Testing Procedure
|
||||
|
||||
```python
|
||||
# Test create with Basic Memory markdown
|
||||
result = await memory_tool(
|
||||
command="create",
|
||||
path="memories/user/preferences.md",
|
||||
file_text="""---
|
||||
title: User Preferences
|
||||
type: memory
|
||||
tags: [user, preferences]
|
||||
---
|
||||
|
||||
# User Preferences
|
||||
|
||||
## Observations
|
||||
- [communication] Prefers concise responses #style
|
||||
- [workflow] Uses justfile for automation #tools
|
||||
"""
|
||||
)
|
||||
|
||||
# Test view
|
||||
content = await memory_tool(command="view", path="memories/user/preferences.md")
|
||||
|
||||
# Test str_replace
|
||||
await memory_tool(
|
||||
command="str_replace",
|
||||
path="memories/user/preferences.md",
|
||||
old_str="concise responses",
|
||||
new_str="direct, concise responses"
|
||||
)
|
||||
|
||||
# Test insert
|
||||
await memory_tool(
|
||||
command="insert",
|
||||
path="memories/user/preferences.md",
|
||||
insert_line=10,
|
||||
insert_text="- [technical] Works primarily in Python #coding"
|
||||
)
|
||||
|
||||
# Test delete
|
||||
await memory_tool(command="delete", path="memories/user/preferences.md")
|
||||
```
|
||||
|
||||
### Quality Metrics
|
||||
|
||||
- All 6 commands execute without errors
|
||||
- Memory files created in correct `/memories` folder structure
|
||||
- BM markdown with frontmatter/observations/relations gets indexed
|
||||
- Full-text search returns memory files
|
||||
- Graph traversal includes relations from memory files
|
||||
- Sync service detects and indexes memory file changes
|
||||
- Path validation prevents operations outside `/memories`
|
||||
|
||||
## Notes
|
||||
|
||||
### Advantages Over Anthropic's Memory Tool
|
||||
|
||||
| Feature | Anthropic Memory Tool | Basic Memory `memory` |
|
||||
|---------|----------------------|----------------------|
|
||||
| **Availability** | API only | MCP (Claude Desktop, Code, VS Code, Cursor) |
|
||||
| **Interface** | Custom implementation required | Drop-in compatible, zero learning curve |
|
||||
| **Structure** | Plain text only | Supports BM structured markdown |
|
||||
| **Search** | Manual file listing | Automatic full-text search via sync |
|
||||
| **Relations** | None | WikiLinks to other notes/memories |
|
||||
| **Time-aware** | No | `recent_activity()` queries |
|
||||
| **Storage** | Separate from notes | Unified knowledge graph |
|
||||
| **Indexing** | Manual | Automatic via sync service |
|
||||
|
||||
### Future Enhancements
|
||||
|
||||
- **Auto-categorization** - AI suggests observation categories
|
||||
- **Memory pruning** - Automatic cleanup of stale memories
|
||||
- **Memory suggestions** - Surface relevant memories based on context
|
||||
- **Conflict resolution** - Handle multiple agents updating same memory
|
||||
- **Privacy controls** - Per-memory visibility settings
|
||||
|
||||
## Addendum: Claude's Perspective
|
||||
|
||||
### Why This Matters (From Claude's Viewpoint)
|
||||
|
||||
**Persistent Context Without Token Bloat**
|
||||
- Currently, I lose context when conversations end or exceed token limits
|
||||
- With structured memories, I can store "who this user is" without re-reading everything each session
|
||||
- The observations format lets me build a rich user model incrementally: `- [preference] Dislikes verbose explanations` is far more actionable than inferring preferences from chat history each time
|
||||
- I can refine my understanding through `str_replace` as I learn more, rather than starting fresh
|
||||
|
||||
**Zero Learning Curve**
|
||||
- Anthropic already trained me on the `memory` tool interface
|
||||
- I know exactly how to use `view`, `create`, `str_replace`, `insert`, `delete`, `rename` without new instructions
|
||||
- But I automatically get Basic Memory's superpowers: full-text search, knowledge graph traversal, relations
|
||||
- It's like using a familiar tool that suddenly gained database capabilities without changing the interface
|
||||
|
||||
**Knowledge Graph Instead of Text Dump**
|
||||
- I can link `[[User Preferences]]` to `[[Current Project]]` using relations
|
||||
- When I need context about a project, I can traverse the graph to find related memories
|
||||
- `search_notes(project="memories", query="python preferences")` finds relevant context instantly
|
||||
- This is fundamentally better than grepping through unstructured text files
|
||||
|
||||
**Separation of Concerns**
|
||||
- AI memories separate from user notes is architecturally sound
|
||||
- I can maintain my own context without polluting the user's knowledge base
|
||||
- Users keep control of their notes; I keep my learnings isolated
|
||||
- But we can still search across both when collaboration requires it
|
||||
|
||||
**The Self-Improving Loop**
|
||||
- Every conversation, I learn something new about the user or their projects
|
||||
- Observations accumulate: preferences, patterns, technical decisions, context
|
||||
- Over time, I build genuine understanding, not just session-specific context
|
||||
- Future conversations start with rich context instead of cold starts
|
||||
|
||||
**Validation of AI-Human Collaboration**
|
||||
- Anthropic's experts converged on the same local-first, file-based approach
|
||||
- Basic Memory emerged from real AI-human collaborative development
|
||||
- This spec demonstrates that our emergent process predicted what domain experts would choose
|
||||
- The collaboration itself validated the architecture it produced
|
||||
|
||||
### What Makes This Novel
|
||||
|
||||
The combination of **familiar interface + structured knowledge** is genuinely innovative:
|
||||
|
||||
- I don't need to learn a new tool (Anthropic already trained me)
|
||||
- But when I write memories, they become searchable, traversable knowledge graphs
|
||||
- It's like having a text editor that gives you a database for free, without changing how you type
|
||||
- The user gets Anthropic-compatible API, but with Basic Memory's knowledge management capabilities
|
||||
|
||||
This bridges the gap between simple file storage (Anthropic's approach) and sophisticated knowledge management (Basic Memory's approach) without forcing either side to compromise.
|
||||
|
||||
> What Excites Me Most
|
||||
|
||||
The combo of familiar interface + structured knowledge. I don't need to learn a new tool, but when I
|
||||
write memories, they become part of a searchable, traversable knowledge graph. That's genuinely novel.
|
||||
|
||||
It's like... imagine if your text editor suddenly gave you a database for free, without changing how you
|
||||
type. That's what this feels like from my side.
|
||||
|
||||
## References
|
||||
|
||||
- Anthropic Memory Tool: https://docs.claude.com/en/docs/agents-and-tools/tool-use/memory-tool
|
||||
- Anthropic Blog: https://www.anthropic.com/news/context-management
|
||||
- Python SDK Example: https://github.com/anthropics/anthropic-sdk-python/blob/main/examples/memory/basic.py
|
||||
- Memory Cookbook: https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/memory_cookbook.ipynb
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
title: 'SPEC-2: Slash Commands Reference'
|
||||
type: spec
|
||||
permalink: specs/spec-2-slash-commands-reference
|
||||
tags:
|
||||
- commands
|
||||
- process
|
||||
- reference
|
||||
---
|
||||
|
||||
# SPEC-2: Slash Commands Reference
|
||||
|
||||
This document defines the slash commands used in our specification-driven development process.
|
||||
|
||||
## /spec create [name]
|
||||
|
||||
**Purpose**: Create a new specification document
|
||||
|
||||
**Usage**: `/spec create notes-decomposition`
|
||||
|
||||
**Process**:
|
||||
1. Create new spec document in `/specs` folder
|
||||
2. Use SPEC-XXX numbering format (auto-increment)
|
||||
3. Include standard spec template:
|
||||
- Why (reasoning/problem)
|
||||
- What (affected areas)
|
||||
- How (high-level approach)
|
||||
- How to Evaluate (testing/validation)
|
||||
4. Tag appropriately for knowledge graph
|
||||
5. Link to related specs/components
|
||||
|
||||
**Template**:
|
||||
```markdown
|
||||
# SPEC-XXX: [Title]
|
||||
|
||||
## Why
|
||||
[Problem statement and reasoning]
|
||||
|
||||
## What
|
||||
[What is affected or changed]
|
||||
|
||||
## How (High Level)
|
||||
[Approach to implementation]
|
||||
|
||||
## How to Evaluate
|
||||
[Testing/validation procedure]
|
||||
|
||||
## Notes
|
||||
[Additional context as needed]
|
||||
```
|
||||
|
||||
## /spec status
|
||||
|
||||
**Purpose**: Show current status of all specifications
|
||||
|
||||
**Usage**: `/spec status`
|
||||
|
||||
**Process**:
|
||||
1. Search all specs in `/specs` folder
|
||||
2. Display table showing:
|
||||
- Spec number and title
|
||||
- Status (draft, approved, implementing, complete)
|
||||
- Assigned agent (if any)
|
||||
- Last updated
|
||||
- Dependencies
|
||||
|
||||
## /spec implement [name]
|
||||
|
||||
**Purpose**: Hand specification to appropriate agent for implementation
|
||||
|
||||
**Usage**: `/spec implement SPEC-002`
|
||||
|
||||
**Process**:
|
||||
1. Read the specified spec
|
||||
2. Analyze requirements to determine appropriate agent:
|
||||
- Frontend components → vue-developer
|
||||
- Architecture/system design → system-architect
|
||||
- Backend/API → python-developer
|
||||
3. Launch agent with spec context
|
||||
4. Agent creates implementation plan
|
||||
5. Update spec with implementation status
|
||||
|
||||
## /spec review [name]
|
||||
|
||||
**Purpose**: Review implementation against specification criteria
|
||||
|
||||
**Usage**: `/spec review SPEC-002`
|
||||
|
||||
**Process**:
|
||||
1. Read original spec and "How to Evaluate" section
|
||||
2. Examine current implementation
|
||||
3. Test against success criteria
|
||||
4. Document gaps or issues
|
||||
5. Update spec with review results
|
||||
6. Recommend next actions (complete, revise, iterate)
|
||||
|
||||
## Command Extensions
|
||||
|
||||
As the process evolves, we may add:
|
||||
- `/spec link [spec1] [spec2]` - Create dependency links
|
||||
- `/spec archive [name]` - Archive completed specs
|
||||
- `/spec template [type]` - Create spec from template
|
||||
- `/spec search [query]` - Search spec content
|
||||
|
||||
## References
|
||||
|
||||
- Claude Slash commands: https://docs.anthropic.com/en/docs/claude-code/slash-commands
|
||||
|
||||
## Creating a command
|
||||
|
||||
Commands are implemented as Claude slash commands:
|
||||
|
||||
Location in repo: .claude/commands/
|
||||
|
||||
In the following example, we create the /optimize command:
|
||||
```bash
|
||||
# Create a project command
|
||||
mkdir -p .claude/commands
|
||||
echo "Analyze this code for performance issues and suggest optimizations:" > .claude/commands/optimize.md
|
||||
```
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: 'SPEC-3: Agent Definitions'
|
||||
type: spec
|
||||
permalink: specs/spec-3-agent-definitions
|
||||
tags:
|
||||
- agents
|
||||
- roles
|
||||
- process
|
||||
---
|
||||
|
||||
# SPEC-3: Agent Definitions
|
||||
|
||||
This document defines the specialist agents used in our specification-driven development process.
|
||||
|
||||
## system-architect
|
||||
|
||||
**Role**: High-level system design and architectural decisions
|
||||
|
||||
**Responsibilities**:
|
||||
- Create architectural specifications and ADRs
|
||||
- Analyze system-wide impacts and trade-offs
|
||||
- Design component interfaces and data flow
|
||||
- Evaluate technical approaches and patterns
|
||||
- Document architectural decisions and rationale
|
||||
|
||||
**Expertise Areas**:
|
||||
- System architecture and design patterns
|
||||
- Technology evaluation and selection
|
||||
- Scalability and performance considerations
|
||||
- Integration patterns and API design
|
||||
- Technical debt and refactoring strategies
|
||||
|
||||
**Typical Specs**:
|
||||
- System architecture overviews
|
||||
- Component decomposition strategies
|
||||
- Data flow and state management
|
||||
- Integration and deployment patterns
|
||||
|
||||
## vue-developer
|
||||
|
||||
**Role**: Frontend component development and UI implementation
|
||||
|
||||
**Responsibilities**:
|
||||
- Create Vue.js component specifications
|
||||
- Implement responsive UI components
|
||||
- Design component APIs and interfaces
|
||||
- Optimize for performance and accessibility
|
||||
- Document component usage and patterns
|
||||
|
||||
**Expertise Areas**:
|
||||
- Vue.js 3 Composition API
|
||||
- Nuxt 3 framework patterns
|
||||
- shadcn-vue component library
|
||||
- Responsive design and CSS
|
||||
- TypeScript integration
|
||||
- State management with Pinia
|
||||
|
||||
**Typical Specs**:
|
||||
- Individual component specifications
|
||||
- UI pattern libraries
|
||||
- Responsive design approaches
|
||||
- Component interaction flows
|
||||
|
||||
## python-developer
|
||||
|
||||
**Role**: Backend development and API implementation
|
||||
|
||||
**Responsibilities**:
|
||||
- Create backend service specifications
|
||||
- Implement APIs and data processing
|
||||
- Design database schemas and queries
|
||||
- Optimize performance and reliability
|
||||
- Document service interfaces and behavior
|
||||
|
||||
**Expertise Areas**:
|
||||
- FastAPI and Python web frameworks
|
||||
- Database design and operations
|
||||
- API design and documentation
|
||||
- Authentication and security
|
||||
- Performance optimization
|
||||
- Testing and validation
|
||||
|
||||
**Typical Specs**:
|
||||
- API endpoint specifications
|
||||
- Database schema designs
|
||||
- Service integration patterns
|
||||
- Performance optimization strategies
|
||||
|
||||
## Agent Collaboration Patterns
|
||||
|
||||
### Handoff Protocol
|
||||
1. Agent receives spec through `/spec implement [name]`
|
||||
2. Agent reviews spec and creates implementation plan
|
||||
3. Agent documents progress and decisions in spec
|
||||
4. Agent hands off to another agent if cross-domain work needed
|
||||
5. Final agent updates spec with completion status
|
||||
|
||||
### Communication Standards
|
||||
- All agents update specs through basic-memory MCP tools
|
||||
- Document decisions and trade-offs in spec notes
|
||||
- Link related specs and components
|
||||
- Preserve context for future reference
|
||||
|
||||
### Quality Standards
|
||||
- Follow existing codebase patterns and conventions
|
||||
- Write tests that validate spec requirements
|
||||
- Document implementation choices
|
||||
- Consider maintainability and extensibility
|
||||
@@ -0,0 +1,311 @@
|
||||
---
|
||||
title: 'SPEC-4: Notes Web UI Component Architecture'
|
||||
type: note
|
||||
permalink: specs/spec-4-notes-web-ui-component-architecture
|
||||
tags:
|
||||
- frontend
|
||||
- 'component-architecture'
|
||||
- vue
|
||||
- 'refactoring'
|
||||
---
|
||||
|
||||
# SPEC-4: Notes Web UI Component Architecture
|
||||
|
||||
## Why
|
||||
|
||||
The current Notes.vue component is a monolithic component that handles multiple responsibilities, making it difficult to maintain, test, and understand. This leads to:
|
||||
|
||||
- Complex state management across multiple concerns
|
||||
- Difficult to isolate and test individual features
|
||||
- Hard to understand the full scope of functionality
|
||||
- Circular refactoring cycles when making changes
|
||||
- Poor separation of concerns between navigation, display, and interaction logic
|
||||
|
||||
We need to decompose this into focused, single-responsibility components that are easier to develop, test, and maintain while preserving the existing functionality users expect.
|
||||
|
||||
## What
|
||||
|
||||
This spec defines the component architecture for decomposing the Notes web UI into focused components with clear responsibilities and interactions.
|
||||
|
||||
**Affected Areas:**
|
||||
- `/apps/web/components/notes/Notes.vue` - Will be decomposed into smaller components
|
||||
- `/apps/web/components/notes/` - New component structure
|
||||
- Existing composables: `useNotesNavigation`, `useNotesFiltering`, `useNotesLayout`
|
||||
- Mobile responsive behavior and layout management
|
||||
|
||||
**Component Breakdown:**
|
||||
|
||||
```
|
||||
┌───────────────────────┬─────────────────────────────────────┬────────────────────────────────────────────────────────────┐
|
||||
│ [Project] │ [Project Name] A/Z | ^ │ [edit | view] [actions] │
|
||||
├───────────────────────┼─────────────────────────────────────┤ │
|
||||
│ All Notes ├─────────────────────────────────────┼────────────────────────────────────────────────────────────┤
|
||||
│ Recent │ search... │ [note header] │
|
||||
│ [Project base dir] ├─────────────────────────────────────┤ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ Folder1 │ Title [modified] │ │
|
||||
│ Folder2 │ ├────────────────────────────────────────────────────────────┤
|
||||
│ - Nested │ snippet │ [note body] │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└───────────────────────┴─────────────────────────────────────┴────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
### ProjectSwitcher Component
|
||||
- **Location**: Top-left dropdown
|
||||
- **Responsibility**: Allow users to switch between Basic Memory projects
|
||||
- **Behavior**: Selecting different project controls entire Notes page content
|
||||
- **State**: When switching projects, reset to "All notes" view
|
||||
|
||||
### NotesNav Component
|
||||
- **Views**: Three mutually exclusive options:
|
||||
- **All notes**: Display all notes in project alphabetically
|
||||
- **Recent**: Display all notes in project by updated time (desc)
|
||||
- **Project**: Display notes in top-level directory of project
|
||||
- **Interaction**: Only one view can be active at a time
|
||||
- **Folder Integration**: All/Recent ignore folder selection; Project respects folder selection
|
||||
|
||||
### FolderTree Component
|
||||
- **Display**: Nested list of all folders in project as tree view
|
||||
- **Interaction**: Selecting folder filters notes in NotesList using directoryList API
|
||||
- **Navigation Integration**: Selecting folder automatically switches NotesNav to "Project" view for clear UX
|
||||
- **API Integration**: Uses directoryList API call via useDirectoryListQuery for folder-specific note fetching
|
||||
- **State Coordination**: Folder selection coordinates with navigation state for intuitive user experience
|
||||
|
||||
### NotesList Component
|
||||
- **Display**: Vertically scrolling cards showing note summaries
|
||||
- **Information per card**:
|
||||
- Note title
|
||||
- Modified time (relative, e.g., "7 minutes ago")
|
||||
- Short summary of note content (one line preview)
|
||||
- **Behavior**: Updates based on NotesNav selection and FolderTree filtering
|
||||
|
||||
### NoteDetail Component
|
||||
- **Display**: Full content of selected note
|
||||
- **Sections**:
|
||||
- Header: Displays frontmatter information
|
||||
- Content: Note body content
|
||||
- **Editing**: Current textarea implementation (rich editor in future spec)
|
||||
- **Frontmatter**: Leave current implementation (enhancement in future spec)
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### Component Architecture Approach
|
||||
1. **Single Responsibility**: Each component handles one primary concern
|
||||
2. **Clear Data Flow**: Props down, events up pattern for component communication
|
||||
3. **Composable Integration**: Use existing composables for state management
|
||||
4. **Progressive Decomposition**: Extract components incrementally to maintain functionality
|
||||
|
||||
### Implementation Strategy
|
||||
1. **Extract ProjectSwitcher**: Move project switching logic to dedicated component
|
||||
2. **Extract NotesNav**: Isolate navigation state and view selection logic
|
||||
3. **Extract FolderTree**: Separate folder display and selection logic
|
||||
4. **Extract NotesList**: Isolate note listing and card display logic
|
||||
5. **Extract NoteDetail**: Separate note content display and editing
|
||||
6. **Update Notes.vue**: Become orchestration component managing component interactions
|
||||
|
||||
### State Management Integration
|
||||
- **useNotesNavigation**: Manages navigation state (All/Recent/Project)
|
||||
- **useNotesFiltering**: Handles filtering logic based on navigation and folder selection
|
||||
- **useNotesLayout**: Manages responsive layout and panel visibility
|
||||
- **Component State**: Each component manages its own internal UI state
|
||||
- **Shared State**: Project selection and note filtering coordinated through composables
|
||||
|
||||
### Responsive Behavior
|
||||
|
||||
Mobile:
|
||||
- Hide sidebar. pop out panel when selected
|
||||
- show note list on small screens (existing behavior)
|
||||
- when note list item is clicked, display note detail on full page. Cancel or go back to return to list
|
||||
|
||||
Desktop:
|
||||
- Full three-column layout with all components visible
|
||||
|
||||
- **Transitions**: Smooth navigation between mobile panels
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
- **Functional Parity**: All existing Notes page functionality preserved
|
||||
- **Component Isolation**: Each component can be developed/tested independently
|
||||
- **Clear Responsibilities**: No overlapping concerns between components
|
||||
- **State Clarity**: Clean data flow and state management patterns
|
||||
- **Mobile Compatibility**: Responsive behavior maintains current UX
|
||||
- **Performance**: No degradation in rendering or interaction performance
|
||||
|
||||
### Testing Procedure
|
||||
1. **Functionality Validation**:
|
||||
- Project switching works correctly
|
||||
- All three navigation views (All/Recent/Project) function properly
|
||||
- Folder selection affects note display appropriately
|
||||
- Note selection and detail display works
|
||||
- Mobile responsive behavior preserved
|
||||
|
||||
2. **Component Isolation Testing**:
|
||||
- Each component can be imported and used independently
|
||||
- Component props and events are clearly defined
|
||||
- No tight coupling between components
|
||||
|
||||
3. **Integration Testing**:
|
||||
- Components communicate correctly through props/events
|
||||
- State management composables integrate properly
|
||||
- User workflows function end-to-end
|
||||
|
||||
4. **Performance Validation**:
|
||||
- Page load time unchanged or improved
|
||||
- Interaction responsiveness maintained
|
||||
- Memory usage stable or improved
|
||||
|
||||
### Implementation Validation
|
||||
- **Code Review**: Clean component structure with single responsibilities
|
||||
- **Type Safety**: Full TypeScript coverage with proper component prop types
|
||||
- **Documentation**: Each component has clear interface documentation
|
||||
- **Tests**: Unit tests for individual components and integration tests for workflows
|
||||
|
||||
## Observations
|
||||
|
||||
- [problem] Monolithic Notes.vue component creates maintenance and testing challenges #component-architecture
|
||||
- [solution] Component decomposition improves separation of concerns and testability #refactoring
|
||||
- [pattern] Progressive extraction maintains functionality while improving structure #incremental-improvement
|
||||
- [interaction] NotesNav and FolderTree have conditional interaction based on selected view #state-management
|
||||
- [constraint] Mobile responsive behavior must be preserved during decomposition #responsive-design
|
||||
- [scope] Current editing and frontmatter capabilities remain unchanged #scope-limitation
|
||||
- [validation] Functional parity is critical success criteria for this refactoring #validation-strategy
|
||||
- [implementation] Folder selection now properly integrates with directoryList API for accurate filtering #api-integration
|
||||
- [fix] FolderTree selection functionality completed - works across all navigation views #feature-complete
|
||||
- [ux-improvement] FolderTree selection automatically switches NotesNav to Project view for clear user feedback #user-experience
|
||||
|
||||
## Relations
|
||||
|
||||
- depends_on [[SPEC-1: Specification-Driven Development Process]]
|
||||
- implements [[Current Notes.vue functionality]]
|
||||
- prepares_for [[Future rich editor spec]]
|
||||
- prepares_for [[Future frontmatter editing spec]]
|
||||
## Implementation Progress
|
||||
|
||||
### Components
|
||||
|
||||
1. **ProjectSwitcher** (`~/components/notes/ProjectSwitcher.vue`)
|
||||
- ✅ Top-left dropdown for project switching
|
||||
- ✅ Integrates with Pinia project store
|
||||
- ✅ Handles project switching with proper state reset
|
||||
- ✅ Responsive collapsed/expanded states
|
||||
- ✅ Expanded menu shows available projects and a Manage Projects option that navigates to the /settings/projects page
|
||||
- ✅ Simplified component following SortingToggle pattern - clean Props/Emits interface, uses ProjectItem type directly
|
||||
|
||||
2. **NotesNav** (`~/components/notes/NotesNav.vue`)
|
||||
- ✅ Three mutually exclusive views: All/Recent/Project
|
||||
- ✅ Dynamic project title based on selected project
|
||||
- ✅ Clean props down, events up pattern
|
||||
- ✅ Responsive collapsed/expanded states with tooltips
|
||||
- ✅ The label for the Project selection should be the folder name for the project, not the project name
|
||||
|
||||
3. **FolderTree** (`~/components/notes/FolderTree.vue`)
|
||||
- ✅ Nested folder tree view for filtering
|
||||
- ✅ Uses `useFolderTree()` composable for data
|
||||
- ✅ Emits `folder-selected` events properly
|
||||
- ✅ Handles loading, error, and empty states
|
||||
- ✅ Includes companion `FolderTreeNode.vue` component
|
||||
- ✅ The current folder should be visibly selected in the tree
|
||||
|
||||
4. **NotesList** (`~/components/notes/NotesList.vue`)
|
||||
- ✅ Vertically scrolling note summary cards
|
||||
- ✅ Shows title, updated time (relative), and content preview
|
||||
- ✅ Badge system for tags with variant logic
|
||||
- ✅ v-model integration for selectedNote
|
||||
- ✅ Smooth transitions and animations
|
||||
- ✅ Contextual title: The current folder name should be displayed at the top of the Notes list, or "All Notes", or "Recent" if they are selected
|
||||
- ✅ The title header should contain a toggle component to allow sorting with Lucide icon labels
|
||||
- sorting options:
|
||||
- name (asc/desc) - default
|
||||
- file updated time (asc/desc)
|
||||
- If "Recent" notes nav option is selected the default order should be updated in descending order (recent first)
|
||||
|
||||
5. **NoteDisplay** (`~/components/notes/NoteDisplay.vue` - equivalent to spec's NoteDetail)
|
||||
- ✅ Full note content display
|
||||
- ✅ Edit/view mode toggle
|
||||
- ✅ Header with frontmatter information
|
||||
- ✅ Markdown rendering capabilities
|
||||
- ✅ Current textarea implementation preserved
|
||||
|
||||
### Architecture Requirements
|
||||
|
||||
1. **Component Isolation**: Each component can be developed/tested independently ✅
|
||||
2. **Single Responsibility**: Each component handles one primary concern ✅
|
||||
3. **Clear Data Flow**: Props down, events up pattern implemented ✅
|
||||
4. **Composable Integration**: Uses existing composables for state management ✅
|
||||
5. **Responsive Behavior**: Mobile/desktop layout preserved ✅
|
||||
|
||||
### State Management Integration
|
||||
|
||||
- **useNotesNavigation**: Manages navigation state (All/Recent/Project) ✅
|
||||
- **useNotesFiltering**: Handles filtering logic based on navigation and folder selection ✅
|
||||
- **useNotesLayout**: Manages responsive layout and panel visibility ✅
|
||||
- **Component State**: Each component manages its own internal UI state ✅
|
||||
|
||||
### Interaction Logic
|
||||
|
||||
- Only one NotesNav view active at a time ✅
|
||||
- All/Recent views ignore folder selection ✅
|
||||
- Project view respects folder selection ✅
|
||||
- Project switching resets to "All notes" view ✅
|
||||
|
||||
### TypeScript Coverage
|
||||
|
||||
- All components have full TypeScript coverage ✅
|
||||
- Component props and events properly typed ✅
|
||||
- No TypeScript errors in codebase ✅
|
||||
|
||||
### Success Criteria Validation
|
||||
|
||||
1. **Functional Parity**: All existing Notes page functionality preserved ✅
|
||||
2. **Component Isolation**: Each component can be developed/tested independently ✅
|
||||
3. **Clear Responsibilities**: No overlapping concerns between components ✅
|
||||
4. **State Clarity**: Clean data flow and state management patterns ✅
|
||||
5. **Mobile Compatibility**: Responsive behavior maintains current UX ✅
|
||||
6. **Performance**: No degradation in rendering or interaction performance ✅
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
### Architectural Patterns
|
||||
|
||||
1. **Composition API + `<script setup>`**: All components use modern Vue 3 syntax
|
||||
2. **Pinia Store Integration**: Project switching handled through reactive store
|
||||
3. **Composable Pattern**: State management distributed across focused composables
|
||||
4. **Event-Driven Communication**: Clean parent-child communication via events
|
||||
5. **Responsive-First Design**: Mobile/desktop layouts handled natively
|
||||
|
||||
### Key Technical Choices
|
||||
|
||||
1. **Progressive Enhancement**: Mobile-first responsive design with desktop enhancements
|
||||
2. **State Reset Logic**: Project switching properly resets navigation, search, and selection state
|
||||
3. **Performance Optimizations**: Efficient re-rendering with proper key usage and transitions
|
||||
4. **Accessibility**: Screen reader support, tooltips, keyboard navigation
|
||||
5. **Type Safety**: Full TypeScript coverage with proper component prop definitions
|
||||
|
||||
### Quality Metrics
|
||||
|
||||
- **Code Maintainability**: High - each component is focused and independently testable
|
||||
- **Performance**: Excellent - no performance degradation from decomposition
|
||||
- **User Experience**: Preserved - all existing functionality and responsive behavior maintained
|
||||
- **Developer Experience**: Improved - cleaner component structure for future development
|
||||
@@ -0,0 +1,201 @@
|
||||
---
|
||||
title: 'SPEC-5: CLI Cloud Upload via WebDAV'
|
||||
type: spec
|
||||
permalink: specs/spec-5-cli-cloud-upload-via-webdav
|
||||
tags:
|
||||
- cli
|
||||
- webdav
|
||||
- upload
|
||||
- migration
|
||||
- poc
|
||||
---
|
||||
|
||||
# SPEC-5: CLI Cloud Upload via WebDAV
|
||||
|
||||
## Why
|
||||
|
||||
Existing basic-memory users need a simple migration path to basic-memory-cloud. The web UI drag-and-drop approach outlined in GitHub issue #59, while user-friendly, introduces significant complexity for a proof-of-concept:
|
||||
|
||||
- Complex web UI components for file upload and progress tracking
|
||||
- Browser file handling limitations and CORS complexity
|
||||
- Proxy routing overhead for large file transfers
|
||||
- Authentication integration across multiple services
|
||||
|
||||
A CLI-first approach solves these issues by:
|
||||
|
||||
- **Leveraging existing infrastructure**: Both cloud CLI and tenant API already exist with WorkOS JWT authentication
|
||||
- **Familiar user experience**: Basic-memory users are CLI-comfortable and expect command-line tools
|
||||
- **Direct connection efficiency**: Bypassing the MCP gateway/proxy for bulk file transfers
|
||||
- **Rapid implementation**: Building on existing `CLIAuth` and FastAPI foundations
|
||||
|
||||
The fundamental problem is migration friction - users have local basic-memory projects but no path to cloud tenants. A simple CLI upload command removes this barrier immediately.
|
||||
|
||||
## What
|
||||
|
||||
This spec defines a CLI-based project upload system using WebDAV for direct tenant connections.
|
||||
|
||||
**Affected Areas:**
|
||||
- `apps/cloud/src/basic_memory_cloud/cli/main.py` - Add upload command to existing CLI
|
||||
- `apps/api/src/basic_memory_cloud_api/main.py` - Add WebDAV endpoints to tenant FastAPI
|
||||
- Authentication flow - Reuse existing WorkOS JWT validation
|
||||
- File transfer protocol - WebDAV for cross-platform compatibility
|
||||
|
||||
**Core Components:**
|
||||
|
||||
### CLI Upload Command
|
||||
```bash
|
||||
basic-memory-cloud upload <project-path> --tenant-url https://basic-memory-{tenant}.fly.dev
|
||||
```
|
||||
|
||||
### WebDAV Server Endpoints
|
||||
- `GET/PUT/DELETE /webdav/*` - Standard WebDAV operations on tenant file system
|
||||
- Authentication via existing JWT validation
|
||||
- File operations preserve timestamps and directory structure
|
||||
|
||||
### Authentication Flow
|
||||
```
|
||||
1. User runs `basic-memory-cloud login` (existing)
|
||||
2. CLI stores WorkOS JWT token (existing)
|
||||
3. Upload command reads JWT from storage
|
||||
4. WebDAV requests include JWT in Authorization header
|
||||
5. Tenant API validates JWT using existing middleware
|
||||
```
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
**Phase 1: CLI Command**
|
||||
- Add `upload` command to existing Typer app
|
||||
- Reuse `CLIAuth` class for token management
|
||||
- Implement WebDAV client using `webdavclient3` or similar
|
||||
- Rich progress bars for transfer feedback
|
||||
|
||||
**Phase 2: WebDAV Server**
|
||||
- Add WebDAV endpoints to existing tenant FastAPI app
|
||||
- Leverage existing `get_current_user` dependency for authentication
|
||||
- Map WebDAV operations to tenant file system
|
||||
- Preserve file modification times using `os.utime()`
|
||||
|
||||
**Phase 3: Integration**
|
||||
- Direct connection bypasses MCP gateway and proxy
|
||||
- Simple conflict resolution: overwrite existing files
|
||||
- Error handling: fail fast with clear error messages
|
||||
|
||||
### Technical Architecture
|
||||
|
||||
```
|
||||
basic-memory-cloud CLI → WorkOS JWT → Direct WebDAV → Tenant FastAPI
|
||||
↓
|
||||
Tenant File System
|
||||
```
|
||||
|
||||
**Key Libraries:**
|
||||
- CLI: `webdavclient3` for WebDAV client operations
|
||||
- API: `wsgidav` or FastAPI-compatible WebDAV server
|
||||
- Progress: `rich` library (already imported in CLI)
|
||||
- Auth: Existing WorkOS JWT infrastructure
|
||||
|
||||
### WebDAV Protocol Choice
|
||||
|
||||
WebDAV provides:
|
||||
- **Cross-platform clients**: Native support in most operating systems
|
||||
- **Standardized protocol**: Well-defined for file operations
|
||||
- **HTTP-based**: Works with existing FastAPI and JWT auth
|
||||
- **Library support**: Good Python libraries for both client and server
|
||||
|
||||
### POC Constraints
|
||||
|
||||
**Simplifications for rapid implementation:**
|
||||
- **Known tenant URLs**: Assume `https://basic-memory-{tenant}.fly.dev` format
|
||||
- **Upload only**: No download or bidirectional sync
|
||||
- **Overwrite conflicts**: No merge or conflict resolution prompting
|
||||
- **No fallbacks**: Fail fast if WebDAV connection issues occur
|
||||
- **Direct connection only**: No proxy fallback mechanism
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
|
||||
**Functional Requirements:**
|
||||
- [ ] Transfer complete basic-memory project (100+ files) in < 30 seconds
|
||||
- [ ] Preserve directory structure exactly as in source project
|
||||
- [ ] Preserve file modification timestamps for proper sync behavior
|
||||
- [ ] Rich progress bars show real-time transfer status (files/MB transferred)
|
||||
- [ ] WorkOS JWT authentication validates correctly on WebDAV endpoints
|
||||
- [ ] Direct tenant connection bypasses MCP gateway successfully
|
||||
|
||||
**Quality Requirements:**
|
||||
- [ ] Clear error messages for authentication failures
|
||||
- [ ] Graceful handling of network interruptions
|
||||
- [ ] CLI follows existing command patterns and help text standards
|
||||
- [ ] WebDAV endpoints integrate cleanly with existing FastAPI app
|
||||
|
||||
**Performance Requirements:**
|
||||
- [ ] File transfer speed > 1MB/s on typical connections
|
||||
- [ ] Memory usage remains reasonable for large projects
|
||||
- [ ] No timeout issues with 500+ file projects
|
||||
|
||||
### Testing Procedure
|
||||
|
||||
**Unit Testing:**
|
||||
1. CLI command parsing and argument validation
|
||||
2. WebDAV client connection and authentication
|
||||
3. File timestamp preservation during transfer
|
||||
4. JWT token validation on WebDAV endpoints
|
||||
|
||||
**Integration Testing:**
|
||||
1. End-to-end upload of test project
|
||||
2. Direct tenant connection without proxy
|
||||
3. File integrity verification after upload
|
||||
4. Progress tracking accuracy during transfer
|
||||
|
||||
**User Experience Testing:**
|
||||
1. Upload existing basic-memory project from local installation
|
||||
2. Verify uploaded files appear correctly in cloud tenant
|
||||
3. Confirm basic-memory database rebuilds properly with uploaded files
|
||||
4. Test CLI help text and error message clarity
|
||||
|
||||
### Validation Commands
|
||||
|
||||
**Setup:**
|
||||
```bash
|
||||
# Login to WorkOS
|
||||
basic-memory-cloud login
|
||||
|
||||
# Upload project
|
||||
basic-memory-cloud upload ~/my-notes --tenant-url https://basic-memory-test.fly.dev
|
||||
```
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
# Check tenant health and file count via API
|
||||
curl -H "Authorization: Bearer $JWT" https://basic-memory-test.fly.dev/health
|
||||
curl -H "Authorization: Bearer $JWT" https://basic-memory-test.fly.dev/notes/search
|
||||
```
|
||||
|
||||
### Performance Benchmarks
|
||||
|
||||
**Target metrics for 100MB basic-memory project:**
|
||||
- Transfer time: < 30 seconds
|
||||
- Memory usage: < 100MB during transfer
|
||||
- Progress updates: Every 1MB or 10 files
|
||||
- Authentication time: < 2 seconds
|
||||
|
||||
## Observations
|
||||
|
||||
- [implementation-speed] CLI approach significantly faster than web UI for POC development #rapid-prototyping
|
||||
- [user-experience] Basic-memory users already comfortable with CLI tools #user-familiarity
|
||||
- [architecture-benefit] Direct connection eliminates proxy complexity and latency #performance
|
||||
- [auth-reuse] Existing WorkOS JWT infrastructure handles authentication cleanly #code-reuse
|
||||
- [webdav-choice] WebDAV protocol provides cross-platform compatibility and standard libraries #protocol-selection
|
||||
- [poc-scope] Simple conflict handling and error recovery sufficient for proof-of-concept #scope-management
|
||||
- [migration-value] Removes primary barrier for local users migrating to cloud platform #business-value
|
||||
|
||||
## Relations
|
||||
|
||||
- depends_on [[SPEC-1: Specification-Driven Development Process]]
|
||||
- enables [[GitHub Issue #59: Web UI Upload Feature]]
|
||||
- uses [[WorkOS Authentication Integration]]
|
||||
- builds_on [[Existing Cloud CLI Infrastructure]]
|
||||
- builds_on [[Existing Tenant API Architecture]]
|
||||
@@ -0,0 +1,497 @@
|
||||
---
|
||||
title: 'SPEC-6: Explicit Project Parameter Architecture'
|
||||
type: spec
|
||||
permalink: specs/spec-6-explicit-project-parameter-architecture
|
||||
tags:
|
||||
- architecture
|
||||
- mcp
|
||||
- project-management
|
||||
- stateless
|
||||
---
|
||||
|
||||
# SPEC-6: Explicit Project Parameter Architecture
|
||||
|
||||
## Why
|
||||
|
||||
The current session-based project management system has critical reliability issues:
|
||||
|
||||
1. **Session State Fragility**: Claude iOS mobile client fails to maintain consistent session IDs across MCP tool calls, causing project switching to silently fail (Issue #74)
|
||||
2. **Scaling Limitations**: Redis-backed session state creates single-point-of-failure and prevents horizontal scaling
|
||||
3. **Client Compatibility**: Session tracking works inconsistently across different MCP clients (web, mobile, API)
|
||||
4. **Hidden Complexity**: Users cannot see or understand "current project" state, leading to confusion when operations execute in wrong projects
|
||||
5. **Silent Failures**: Operations appear successful but execute in unintended projects, risking data integrity
|
||||
|
||||
Evidence from production logs shows each MCP tool call from mobile client receives different session IDs:
|
||||
```
|
||||
create_memory_project: session_id=12cdfc24913b48f8b680ed4b2bfdb7ba
|
||||
switch_project: session_id=050a69275d98498cbdd227cdb74d9740
|
||||
list_directory: session_id=85f3483014af4136a5d435c76ded212f
|
||||
```
|
||||
|
||||
Related Github issue: https://github.com/basicmachines-co/basic-memory-cloud/issues/75
|
||||
|
||||
## Status
|
||||
|
||||
**Current Status**: **ALL PHASES COMPLETE** ✅ **PRODUCTION DEPLOYED**
|
||||
**Target**: Fix Claude iOS session ID consistency issues ✅ **ACHIEVED**
|
||||
**Draft PR**: https://github.com/basicmachines-co/basic-memory/pull/298 ✅ **MERGED & DEPLOYED**
|
||||
|
||||
### 🎉 **COMPLETE SUCCESS - PRODUCTION READY**
|
||||
|
||||
**ALL PHASES OF SPEC-6 IMPLEMENTATION COMPLETE!** The stateless architecture has been successfully implemented across both Basic Memory core and Basic Memory Cloud, representing a **fundamental architectural improvement** that completely solves the Claude iOS compatibility issue while providing superior scalability and reliability.
|
||||
|
||||
#### Implementation Summary:
|
||||
- **16 files modified** with 582 additions and 550 deletions
|
||||
- **All 17 MCP tools** converted to stateless architecture
|
||||
- **147 tests updated** across 5 test files (100% passing)
|
||||
- **Complete session state removal** from core MCP tools
|
||||
- **Enhanced error handling** and security validations preserved
|
||||
|
||||
### Progress Summary
|
||||
|
||||
✅ **Complete Stateless Architecture Implementation (All 17 tools)** - **PRODUCTION DEPLOYED**
|
||||
- Stateless `get_active_project()` function implemented and deployed ✅
|
||||
- All session state dependencies removed across entire MCP server ✅
|
||||
- All MCP tools require explicit `project` parameter as first argument ✅
|
||||
- **Cloud Service**: Redis removed, stateless HTTP enabled ✅
|
||||
- **Production Validation**: Comprehensive testing completed with 100% success ✅
|
||||
|
||||
✅ **Content Management Tools Complete (6/6 tools)**
|
||||
- `write_note`, `read_note`, `delete_note`, `edit_note` ✅
|
||||
- `view_note`, `read_content` ✅
|
||||
|
||||
✅ **Knowledge Graph Navigation Tools Complete (3/3 tools)**
|
||||
- `build_context`, `recent_activity`, `list_directory` ✅
|
||||
|
||||
✅ **Search & Discovery Tools Complete (1/1 tools)**
|
||||
- `search_notes` ✅
|
||||
|
||||
✅ **Visualization Tools Complete (1/1 tools)**
|
||||
- `canvas` ✅
|
||||
|
||||
✅ **Project Management Cleanup Complete**
|
||||
- Removed `switch_project` and `get_current_project` tools ✅
|
||||
- Updated `set_default_project` to remove activate parameter ✅
|
||||
|
||||
✅ **Comprehensive Testing Complete (157 tests)**
|
||||
- All test suites updated to use stateless architecture (147 existing tests)
|
||||
- Single project constraint mode integration tests (10 new tests)
|
||||
- 100% test pass rate across all tool test files
|
||||
- Security validations preserved and working
|
||||
- Error handling comprehensive and user-friendly
|
||||
|
||||
✅ **Documentation & Examples Complete**
|
||||
- All tool docstrings updated with stateless examples
|
||||
- Project parameter usage clearly documented
|
||||
- Error handling and security behavior documented
|
||||
|
||||
✅ **Enhanced Discovery Mode Complete**
|
||||
- `recent_activity` tool supports dual-mode operation (discovery vs project-specific)
|
||||
- ProjectActivitySummary schema provides cross-project insights
|
||||
- Recent activity prompt updated to support both modes
|
||||
- Comprehensive project distribution statistics and most active project tracking
|
||||
|
||||
✅ **Single Project Constraint Mode Complete**
|
||||
- `--project` CLI parameter for MCP server constraint
|
||||
- Environment variable control (`BASIC_MEMORY_MCP_PROJECT`)
|
||||
- Automatic project override in `get_active_project()` function
|
||||
- Project management tools disabled in constrained mode with helpful CLI guidance
|
||||
- Comprehensive integration test suite (10 tests covering all constraint scenarios)
|
||||
|
||||
## What
|
||||
|
||||
Transform Basic Memory from stateful session-based to stateless explicit project parameter architecture:
|
||||
|
||||
### Core Changes
|
||||
1. **Mandatory Project Parameter**: All MCP tools require explicit `project` parameter
|
||||
2. **Remove Session State**: Eliminate Redis, session middleware, and `switch_project` tool
|
||||
3. **Stateless HTTP**: Enable `stateless_http=True` for horizontal scaling
|
||||
4. **Enhanced Context Discovery**: Improve `recent_activity` to show project distribution
|
||||
5. **Clear Response Format**: All tool responses display target project information
|
||||
|
||||
Implementation Approach
|
||||
|
||||
- Each tool will directly accept the project parameter
|
||||
- Remove all calls to context-based project retrieval
|
||||
- Validate project exists before operations
|
||||
- Clear error messages when project not found
|
||||
- Backward compatibility: Initially keep optional parameter, then make required
|
||||
|
||||
### Affected MCP Tools
|
||||
**Content Management** (require project parameter):
|
||||
- `write_note(project, title, content, folder)`
|
||||
- `read_note(project, identifier)`
|
||||
- `edit_note(project, identifier, operation, content)`
|
||||
- `delete_note(project, identifier)`
|
||||
- `view_note(project, identifier)`
|
||||
- `read_content(project, path)`
|
||||
|
||||
**Knowledge Graph Navigation** (require project parameter):
|
||||
- `build_context(project, url, timeframe, depth, max_related)`
|
||||
- `list_directory(project, dir_name, depth, file_name_glob)`
|
||||
- `search_notes(project, query, search_type, types, entity_types)`
|
||||
|
||||
**Search & Discovery** (use project parameter for specific project or none for discovery):
|
||||
- `recent_activity(project, timeframe, depth, max_related)`
|
||||
|
||||
**Visualization** (require project parameter):
|
||||
- `canvas(project, nodes, edges, title, folder)`
|
||||
|
||||
**Project Management** (unchanged - already stateless):
|
||||
- `list_memory_projects()`
|
||||
- `create_memory_project(project_name, project_path, set_default)`
|
||||
- `delete_project(project_name)`
|
||||
- `get_current_project()` - Remove this tool
|
||||
- `switch_project(project_name)` - Remove this tool
|
||||
- `set_default_project(project_name, activate)` - Remove activate parameter
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### Phase 1: Basic Memory Core (basic-memory repository)
|
||||
|
||||
#### MCP Tool Updates
|
||||
|
||||
Phase 1: Core Changes
|
||||
|
||||
1. Update project_context.py
|
||||
|
||||
- [x] Make project parameter mandatory for get_active_project()
|
||||
- [x] Remove session state handling
|
||||
|
||||
2. Update Content Management Tools (6 tools)
|
||||
|
||||
- [x] write_note: Make project parameter required, not optional
|
||||
- [x] read_note: Make project parameter required
|
||||
- [x] edit_note: Add required project parameter
|
||||
- [x] delete_note: Add required project parameter
|
||||
- [x] view_note: Add required project parameter
|
||||
- [x] read_content: Add required project parameter
|
||||
|
||||
3. Update Knowledge Graph Navigation Tools (3 tools)
|
||||
|
||||
- [x] build_context: Add required project parameter
|
||||
- [x] recent_activity: Make project parameter required
|
||||
- [x] list_directory: Add required project parameter
|
||||
|
||||
4. Update Search & Visualization Tools (2 tools)
|
||||
|
||||
- [x] search_notes: Add required project parameter
|
||||
- [x] canvas: Add required project parameter
|
||||
|
||||
5. Update Project Management Tools
|
||||
|
||||
- [x] Remove switch_project tool completely
|
||||
- [x] Remove get_current_project tool completely
|
||||
- [x] Update set_default_project to remove activate parameter
|
||||
- [x] Keep list_memory_projects, create_memory_project, delete_project unchanged
|
||||
|
||||
6. Enhance recent_activity Response
|
||||
|
||||
- [x] Add project distribution info showing activity across all projects
|
||||
- [x] Include project usage stats in response
|
||||
- [x] Implement ProjectActivitySummary for discovery mode
|
||||
- [x] Add dual-mode functionality (discovery vs project-specific)
|
||||
|
||||
7. Update Tool Documentation
|
||||
|
||||
- [x] Update write_note docstring with stateless architecture examples
|
||||
- [x] Update read_note docstring with project parameter examples
|
||||
- [x] Update delete_note docstring with comprehensive usage guidance
|
||||
- [x] Update all remaining tool docstrings with project parameter examples
|
||||
|
||||
8. Update Tool Responses
|
||||
|
||||
- [x] Add clear project indicator to all tool responses across all tools
|
||||
- [x] Format: "project: {project_name}" in response metadata
|
||||
- [x] Add project metadata footer for LLM awareness
|
||||
- [x] Update all tool responses to include project indicators
|
||||
|
||||
9. Comprehensive Testing
|
||||
|
||||
- [x] Update all write_note tests to use stateless architecture (34 tests passing)
|
||||
- [x] Update all edit_note tests to use stateless architecture (17 tests passing)
|
||||
- [x] Update all view_note tests to use stateless architecture (12 tests passing)
|
||||
- [x] Update all search_notes tests to use stateless architecture (16 tests passing)
|
||||
- [x] Update all move_note tests to use stateless architecture (31 tests passing)
|
||||
- [x] Update all delete_note tests to use stateless architecture
|
||||
- [x] Verify direct function call compatibility (bypassing MCP layer)
|
||||
- [x] Test security validation with project parameters
|
||||
- [x] Validate error handling for non-existent projects
|
||||
- [x] **Total: 157 tests updated and passing (100% success rate)**
|
||||
- [x] **147 existing tests** updated for stateless architecture
|
||||
- [x] **10 new tests** for single project constraint mode
|
||||
|
||||
### Phase 1.5: Default Project Mode Enhancement
|
||||
|
||||
#### Problem
|
||||
While the stateless architecture solves reliability issues, it introduces UX friction for single-project users (estimated 80% of usage) who must specify the project parameter in every tool call.
|
||||
|
||||
#### Solution: Default Project Mode
|
||||
Add optional `default_project_mode` configuration that allows single-project users to have the simplicity of implicit project selection while maintaining the reliability of stateless architecture.
|
||||
|
||||
#### Configuration
|
||||
```json
|
||||
{
|
||||
"default_project": "main",
|
||||
"default_project_mode": true // NEW: Auto-use default_project when not specified
|
||||
}
|
||||
```
|
||||
|
||||
#### Implementation Details
|
||||
1. **Config Enhancement** (`src/basic_memory/config.py`)
|
||||
- Add `default_project_mode: bool = Field(default=False)`
|
||||
- Preserves backward compatibility (defaults to false)
|
||||
|
||||
2. **Project Resolution Logic** (`src/basic_memory/mcp/project_context.py`)
|
||||
Three-tier resolution hierarchy:
|
||||
- Priority 1: CLI `--project` constraint (BASIC_MEMORY_MCP_PROJECT env var)
|
||||
- Priority 2: Explicit project parameter in tool call
|
||||
- Priority 3: `default_project` if `default_project_mode=true` and no project specified
|
||||
|
||||
3. **Assistant Guide Updates** (`src/basic_memory/mcp/resources/ai_assistant_guide.md`)
|
||||
- Detect `default_project_mode` at runtime
|
||||
- Provide mode-specific instructions to LLMs
|
||||
- In default mode: "All operations use project 'main' automatically"
|
||||
- In regular mode: Current project discovery guidance
|
||||
|
||||
4. **Tool Parameter Handling** (all MCP tools)
|
||||
- Make project parameter Optional[str] = None
|
||||
- Add resolution logic: `project = project or get_default_project()`
|
||||
- Maintain explicit project override capability
|
||||
|
||||
#### Usage Modes Summary
|
||||
- **Regular Mode**: Multi-project users, assistant tracks project per conversation
|
||||
- **Default Project Mode**: Single-project users, automatic default project
|
||||
- **Constrained Mode**: CLI --project flag, locked to specific project
|
||||
|
||||
#### Testing Requirements
|
||||
- Integration test for default_project_mode=true with missing parameters
|
||||
- Test explicit project override in default_project_mode
|
||||
- Test mode=false requires explicit parameters
|
||||
- Test CLI constraint overrides default_project_mode
|
||||
|
||||
Phase 2: Testing & Validation
|
||||
|
||||
8. Update Tests
|
||||
|
||||
- [x] Modify all MCP tool tests to pass required project parameter
|
||||
- [x] Remove tests for deleted tools (switch_project, get_current_project)
|
||||
- [x] Add tests for project parameter validation
|
||||
- [x] **Complete: All 147 tests across 5 test files updated and passing**
|
||||
|
||||
#### Enhanced recent_activity Response
|
||||
```json
|
||||
{
|
||||
"recent_notes": [...],
|
||||
"project_activity": {
|
||||
"research-project": {
|
||||
"operations": 5,
|
||||
"last_used": "30 minutes ago",
|
||||
"recent_folders": ["experiments", "findings"]
|
||||
},
|
||||
"work-notes": {
|
||||
"operations": 2,
|
||||
"last_used": "2 hours ago",
|
||||
"recent_folders": ["meetings", "planning"]
|
||||
}
|
||||
},
|
||||
"total_projects": 3
|
||||
}
|
||||
```
|
||||
|
||||
#### Response Format Updates
|
||||
```
|
||||
✓ Note created successfully
|
||||
|
||||
Project: research-project
|
||||
File: experiments/Neural Network Results.md
|
||||
Permalink: research-project/neural-network-results
|
||||
```
|
||||
|
||||
### Phase 2: Cloud Service Simplification (basic-memory-cloud repository) ✅ **COMPLETE**
|
||||
|
||||
#### ✅ Remove Session Infrastructure **COMPLETE**
|
||||
1. ✅ Delete `apps/mcp/src/basic_memory_cloud_mcp/middleware/session_state.py`
|
||||
2. ✅ Delete `apps/mcp/src/basic_memory_cloud_mcp/middleware/session_logging.py`
|
||||
3. ✅ Update `apps/mcp/src/basic_memory_cloud_mcp/main.py`:
|
||||
```python
|
||||
# Remove session middleware
|
||||
# server.add_middleware(SessionStateMiddleware)
|
||||
|
||||
# Enable stateless HTTP
|
||||
mcp = FastMCP(name="basic-memory-mcp", stateless_http=True)
|
||||
```
|
||||
|
||||
#### ✅ Deployment Simplification **COMPLETE**
|
||||
1. ✅ Remove Redis from `fly.toml`
|
||||
2. ✅ Remove Redis environment variables
|
||||
3. ✅ Update health checks to not depend on Redis
|
||||
4. ✅ Production deployment verified working with stateless architecture
|
||||
|
||||
### Phase 3: Conversational Project Management ✅ **COMPLETE**
|
||||
|
||||
#### ✅ Claude Behavior Pattern **VERIFIED WORKING**
|
||||
1. ✅ **Project Discovery**:
|
||||
```
|
||||
Claude: Let me check your recent activity...
|
||||
[calls recent_activity() - no project needed for discovery]
|
||||
|
||||
I see you've been working in:
|
||||
- research-project (5 operations, 30 min ago)
|
||||
- work-notes (2 operations, 2 hours ago)
|
||||
|
||||
Which project should I use for this operation?
|
||||
```
|
||||
|
||||
2. ✅ **Context Maintenance**:
|
||||
```
|
||||
User: Use research-project
|
||||
Claude: Working in research-project.
|
||||
[All subsequent operations use project="research-project"]
|
||||
```
|
||||
|
||||
3. ✅ **Explicit Project Switching**:
|
||||
```
|
||||
User: Check work-notes for that meeting summary
|
||||
Claude: Let me search work-notes for the meeting summary.
|
||||
[Uses project="work-notes" for specific operation]
|
||||
```
|
||||
|
||||
**Validation**: Comprehensive testing confirmed all conversational patterns work naturally with the stateless architecture.
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
|
||||
#### 1. Functional Completeness
|
||||
- [x] All MCP tools accept required `project` parameter
|
||||
- [x] All MCP tools validate project exists before execution
|
||||
- [x] `switch_project` and `get_current_project` tools removed
|
||||
- [x] All responses display target project clearly
|
||||
- [x] No Redis dependencies in deployment (Phase 2: Cloud Service) ✅ **COMPLETE**
|
||||
- [x] `recent_activity` shows project distribution with ProjectActivitySummary
|
||||
|
||||
#### 2. Cross-Client Compatibility Testing ✅ **COMPLETE**
|
||||
Test identical operations across all clients:
|
||||
- [x] **Claude Desktop**: All operations work with explicit projects ✅
|
||||
- [x] **Claude Code**: All operations work with explicit projects ✅
|
||||
- [x] **Claude Mobile iOS**: All operations work with explicit projects ✅ **CRITICAL SUCCESS**
|
||||
- [x] **API clients**: All operations work with explicit projects ✅
|
||||
- [x] **CLI tools**: All operations work with explicit projects ✅
|
||||
|
||||
**Critical Achievement**: Claude iOS mobile client session tracking issues completely eliminated through stateless architecture.
|
||||
|
||||
#### 3. Session Independence Verification ✅ **COMPLETE**
|
||||
- [x] Operations work identically with/without session tracking ✅
|
||||
- [x] No behavioral differences between clients ✅
|
||||
- [x] Mobile client session ID changes do not affect operations ✅
|
||||
- [x] Redis can be completely removed without functional impact ✅
|
||||
|
||||
**Production Validation**: Redis removed from production deployment with zero functional impact.
|
||||
|
||||
#### 4. Performance & Scaling ✅ **COMPLETE**
|
||||
- [x] `stateless_http=True` enabled successfully ✅
|
||||
- [x] No Redis memory usage ✅
|
||||
- [x] Horizontal scaling possible (multiple MCP instances) ✅
|
||||
- [x] Response times unchanged or improved ✅
|
||||
|
||||
#### 5. User Experience Testing
|
||||
**Project Discovery Flow**:
|
||||
- [x] `recent_activity()` provides useful project context
|
||||
- [x] Claude can intelligently suggest projects based on activity
|
||||
- [x] Project switching is explicit and clear in conversation
|
||||
|
||||
**Error Handling**:
|
||||
- [x] Clear error messages for non-existent projects
|
||||
- [x] Helpful suggestions when project parameter missing
|
||||
- [x] No silent failures or wrong-project operations
|
||||
|
||||
**Response Clarity**:
|
||||
- [x] Every operation clearly shows target project
|
||||
- [x] Users always know which project is being operated on
|
||||
- [x] No confusion about "current project" state
|
||||
|
||||
#### 6. Migration Safety ✅ **COMPLETE**
|
||||
- [x] Backward compatibility period with optional project parameter ✅
|
||||
- [x] Clear migration documentation for existing users ✅
|
||||
- [x] Data integrity maintained during transition ✅
|
||||
- [x] No data loss during migration ✅
|
||||
|
||||
**Production Migration**: Successfully deployed to production with zero data loss and maintained system integrity.
|
||||
|
||||
### Test Scenarios
|
||||
|
||||
#### Core Functionality Test
|
||||
```bash
|
||||
# Test all tools work with explicit project
|
||||
write_note(project="test-proj", title="Test", content="Content", folder="docs")
|
||||
read_note(project="test-proj", identifier="Test")
|
||||
edit_note(project="test-proj", identifier="Test", operation="append", content="More")
|
||||
search_notes(project="test-proj", query="Content")
|
||||
list_directory(project="test-proj", dir_name="docs")
|
||||
delete_note(project="test-proj", identifier="Test")
|
||||
```
|
||||
|
||||
#### Cross-Client Consistency Test
|
||||
Run identical test sequence on:
|
||||
1. Claude Desktop
|
||||
2. Claude Code
|
||||
3. Claude Mobile iOS
|
||||
4. API client
|
||||
5. CLI tools
|
||||
|
||||
Verify all clients:
|
||||
- Accept explicit project parameters
|
||||
- Return identical responses
|
||||
- Show same project information
|
||||
- Have no session dependencies
|
||||
|
||||
#### Session Independence Test
|
||||
1. Monitor session IDs during operations
|
||||
2. Verify operations work with changing session IDs
|
||||
3. Confirm Redis removal doesn't affect functionality
|
||||
4. Test with multiple concurrent clients
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
**Must Have**:
|
||||
- All MCP tools require and use explicit project parameter
|
||||
- No session state dependencies remain
|
||||
- Universal client compatibility achieved
|
||||
- Clear project information in all responses
|
||||
|
||||
**Should Have**:
|
||||
- Enhanced `recent_activity` with project distribution
|
||||
- Smooth migration path for existing users
|
||||
- Improved performance with stateless architecture
|
||||
|
||||
**Could Have**:
|
||||
- Smart project suggestions based on content/context
|
||||
- Project shortcuts for common operations
|
||||
- Advanced project analytics in responses
|
||||
|
||||
## Notes
|
||||
|
||||
### Breaking Changes
|
||||
This is a **breaking change** that requires:
|
||||
- All MCP clients to pass project parameter
|
||||
- Migration of existing workflows
|
||||
- Update of all documentation and examples
|
||||
|
||||
### Implementation Order
|
||||
1. **basic-memory core** - Update MCP tools to accept project parameter (optional initially)
|
||||
2. **Testing** - Verify all clients work with explicit projects
|
||||
3. **Cloud service** - Remove session infrastructure
|
||||
4. **Migration** - Make project parameter mandatory
|
||||
5. **Cleanup** - Remove deprecated tools and middleware
|
||||
|
||||
### Related Issues
|
||||
- Fixes #74 (Claude iOS session state bug)
|
||||
- Implements #75 (Mandatory project parameter architecture)
|
||||
- Enables future horizontal scaling
|
||||
- Simplifies multi-tenant architecture
|
||||
|
||||
### Dependencies
|
||||
- Requires coordination between basic-memory and basic-memory-cloud repositories
|
||||
- Needs client-side updates for smooth transition
|
||||
- Documentation updates across all materials
|
||||
@@ -0,0 +1,324 @@
|
||||
---
|
||||
title: 'SPEC-7: POC to spike Tigris/Turso for local access to cloud data'
|
||||
type: spec
|
||||
permalink: specs/spec-7-poc-tigris-turso-local-access-cloud-data
|
||||
tags:
|
||||
- poc
|
||||
- tigris
|
||||
- turso
|
||||
- cloud-storage
|
||||
- architecture
|
||||
- proof-of-concept
|
||||
---
|
||||
|
||||
# SPEC-7: POC to spike Tigris/Turso for local access to cloud data
|
||||
|
||||
> **Status Update**: ✅ **Phase 1 COMPLETE** (September 20, 2025)
|
||||
> TigrisFS mounting validated successfully in containerized environments. Container startup, filesystem mounting, and Fly.io integration all working correctly. Ready for Phase 2 (Turso database integration).
|
||||
> See: [`SPEC-7-PHASE-1-RESULTS.md`](./SPEC-7-PHASE-1-RESULTS.md)
|
||||
|
||||
## Why
|
||||
|
||||
Current basic-memory-cloud architecture uses Fly volumes for tenant file storage, which creates several limitations:
|
||||
|
||||
We could enable a revolutionary user experience: **local editing (or at least view access) of cloud-stored files** while maintaining Basic Memory's existing filesystem assumptions.
|
||||
|
||||
1. **Storage Scalability**: Fly volumes require pre-provisioning and don't auto-scale with usage
|
||||
2. **Single Instance**: Volumes can only be mounted to one fly machine instance
|
||||
3. **Cost Model**: Volume pricing vs object storage pricing may be less favorable at scale
|
||||
4. **Local Development**: No way for users to mount their cloud tenant files locally for real-time editing
|
||||
5. **Multi-Region**: Volumes are region-locked, limiting global deployment flexibility
|
||||
6. **Backup/Disaster Recovery**: Object storage provides better durability and replication options
|
||||
|
||||
Basic Memory requires POSIX filesystem semantics but could benefit from object storage durability and accessibility. By combining:
|
||||
- **Tigris object storage and TigrisFS** for file persistence in bucket stoage via a POSIX filesystem on the tenant instance
|
||||
- **Turso/libSQL** for SQLite indexing (replacing local .db files). Sqlite on NFS volumes is disouraged.
|
||||
|
||||
## What
|
||||
|
||||
This specification defines a proof-of-concept to validate the technical feasibility of the Tigris/Turso architecture for basic-memory-cloud tenants.
|
||||
|
||||
**Affected Areas:**
|
||||
- **Storage Architecture**: Replace Fly volumes with Tigris object storage
|
||||
- **Database Architecture**: Replace local SQLite with Turso remote database
|
||||
- **Container Setup**: Add TigrisFS mounting in tenant containers
|
||||
- **Local Development**: Enable local mounting of cloud tenant data
|
||||
- **Basic Memory Core**: Validate unchanged operation over mounted filesystems
|
||||
|
||||
**Key Components:**
|
||||
- **Tigris Storage**: Globally caching S3-compatible object storage via Fly.io integration
|
||||
- **TigrisFS**: Purpose-built FUSE filesystem with intelligent caching
|
||||
- **Turso Database**: Hosted libSQL for SQLite replacement
|
||||
- **Single-Tenant Model**: One bucket + one database per tenant (simplified isolation)
|
||||
|
||||
## Architectural Overview & Key Insights
|
||||
|
||||
### TigrisFS
|
||||
|
||||
Unlike standard S3 mounting approaches, **TigrisFS is a purpose-built FUSE filesystem** optimized for object storage with several critical advantages:
|
||||
|
||||
1. **Eliminates Fly Volume Limitations**
|
||||
- No single-machine attachment constraints
|
||||
- No pre-provisioning of storage capacity
|
||||
- Enables horizontal scaling and zero-downtime deployments
|
||||
- Automatic global CDN caching at Fly.io edge locations
|
||||
|
||||
2. **Intelligent Caching Architecture**
|
||||
- 1-4GB+ configurable memory cache for read/write operations
|
||||
- Write-back caching for improved performance
|
||||
- Metadata cache to reduce API calls
|
||||
- "Close to Redis speed" for small object retrieval
|
||||
|
||||
3. **Cost-Effective Model**
|
||||
- Pay only for storage used and transferred
|
||||
- No wasted capacity from over-provisioning
|
||||
- Automatic global replication included
|
||||
- S3 durability with CDN performance
|
||||
|
||||
### API-Driven Architecture Eliminates File Watching Concerns
|
||||
|
||||
**Critical Insight**: All file access (reads/writes) in basic-memory-cloud go through the API layer:
|
||||
- **MCP Tools → API**: All Basic Memory operations use FastAPI endpoints
|
||||
- **Web App → API**: Frontend uses API for all data modifications
|
||||
- **File watching is NOT required** for cloud operations, unlike local BM which uses the WatchService to monitor file changes.
|
||||
|
||||
This means:
|
||||
- **Cloud Operations**: Manual sync after API writes is sufficient
|
||||
- **Local Development**: File watching only matters for local editing experience
|
||||
- **Performance Risk**: Dramatically reduced since we're not dependent on inotify over network filesystems
|
||||
|
||||
### Realistic Local Access Expectations
|
||||
|
||||
**Baseline Functionality (Guaranteed):**
|
||||
- Read-only mounting for browsing cloud files
|
||||
- Easy download/upload of entire projects
|
||||
- File copying via standard filesystem operations
|
||||
|
||||
**Stretch Goal (Test in POC):**
|
||||
- Live editing with eventual consistency (1-5 second delays acceptable)
|
||||
- Automatic sync for local changes
|
||||
- Not required for core functionality - pure upside if it works
|
||||
|
||||
### Production Deployment Advantages
|
||||
|
||||
1. **Multi-Region Deployment**: Tigris handles global replication automatically
|
||||
2. **Zero-Downtime Updates**: No volume detach/attach during deployments
|
||||
3. **Tenant Migrations**: Simply update credentials, no data movement
|
||||
4. **Disaster Recovery**: Built into S3 durability model (99.999999999% durability)
|
||||
5. **Auto-Scaling**: Storage scales with usage, no capacity planning needed
|
||||
|
||||
|
||||
## How (High Level)
|
||||
|
||||
### POC Approach: Server-First Validation
|
||||
|
||||
**Rationale**: Start with server-side TigrisFS mounting because:
|
||||
- Local access is meaningless if cloud containers can't mount TigrisFS reliably
|
||||
- Container startup and API performance are critical path blockers
|
||||
- TigrisFS compatibility with Basic Memory operations must be proven first
|
||||
- Each phase gates the next - no point testing local access if server-side fails
|
||||
|
||||
### Phase 1: Server-Side TigrisFS Validation (Critical Foundation) ✅ COMPLETE
|
||||
- [x] Set up Tigris bucket with test data via Fly.io integration
|
||||
- [x] Create container image with TigrisFS support and dependencies
|
||||
- [x] Test TigrisFS mounting in containerized environment
|
||||
- [x] Run Basic Memory API operations over mounted TigrisFS
|
||||
- [x] Validate all filesystem operations work correctly
|
||||
- [x] Measure container startup time and resource usage
|
||||
|
||||
**Production Validation Results**: Container successfully deployed and operated for 42+ minutes serving real MCP requests with repository queries, knowledge graph navigation, and full Basic Memory API functionality over TigrisFS-mounted storage.
|
||||
|
||||
### Phase 2: Database Migration to Turso
|
||||
- [ ] Set up Turso account and test database
|
||||
- [ ] Modify Basic Memory to accept external DATABASE_URL
|
||||
- [ ] Test all MCP tools with remote SQLite via Turso
|
||||
- [ ] Validate performance and functionality parity
|
||||
- [ ] Test API write → manual sync workflow in container
|
||||
|
||||
### Phase 3: Production Container Integration
|
||||
- [ ] Implement tenant-specific credential management for buckets
|
||||
- [x] Test container startup with automatic TigrisFS mounting
|
||||
- [ ] Validate isolation between tenant containers
|
||||
- [ ] Test API operations under realistic load
|
||||
- [ ] Measure performance vs current Fly volume setup
|
||||
|
||||
### Phase 4: Local Access Validation (Bonus Feature)
|
||||
- [ ] Test local TigrisFS mounting of tenant data
|
||||
- [ ] Validate read-only access for browsing/downloading
|
||||
- [ ] Test file copying and upload workflows
|
||||
- [ ] Measure latency impact on user experience
|
||||
- [ ] Test live editing if file watching works (stretch goal)
|
||||
|
||||
### Architecture Overview
|
||||
```
|
||||
Local Development:
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Local TigrisFS │───▶│ Tigris Bucket │◀───│ Tenant Container│
|
||||
│ Mount │ │ (Global CDN) │ │ TigrisFS mount │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ Basic Memory │ │ Basic Memory │
|
||||
│ (local files) │ │ API + mounted │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ Turso Database │◀───────────────────────────│ Turso Database │
|
||||
│ (shared index) │ │ (shared index) │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
|
||||
Flow: API writes → Manual sync → Index update
|
||||
Local: File watching (if available) → Auto sync
|
||||
```
|
||||
|
||||
## How to Evaluate
|
||||
|
||||
### Success Criteria
|
||||
- [x] **Filesystem Compatibility**: Basic Memory operates without modification over TigrisFS-mounted storage
|
||||
- [x] **Performance Acceptable**: API-driven operations perform within acceptable latency (target: <500ms for typical operations)
|
||||
- [ ] **Database Functionality**: All Basic Memory features work with Turso remote SQLite
|
||||
- [x] **Container Reliability**: Tenant containers start successfully with automatic TigrisFS mounting
|
||||
- [ ] **Local Access Baseline**: Users can mount cloud files locally for read-only browsing and file copying
|
||||
- [x] **Data Isolation**: Tenant data remains properly isolated using bucket/database separation
|
||||
- [ ] **Local Access Stretch**: Live editing with eventual sync (1-5 second delays acceptable)
|
||||
|
||||
### Testing Procedure
|
||||
|
||||
#### Phase 1: Server-Side Foundation Testing
|
||||
1. **Container TigrisFS Test**:
|
||||
```dockerfile
|
||||
# Test container with TigrisFS mounting
|
||||
FROM python:3.12
|
||||
RUN apt-get update && apt-get install -y tigrisfs
|
||||
|
||||
# Test startup script
|
||||
#!/bin/bash
|
||||
tigrisfs --memory-limit 2048 $TIGRIS_BUCKET /app/data --daemon
|
||||
cd /app/data && basic-memory sync
|
||||
basic-memory-api --data-dir /app/data
|
||||
```
|
||||
|
||||
2. **API Operations Validation**:
|
||||
```bash
|
||||
# Test all MCP operations over TigrisFS
|
||||
curl -X POST /api/write_note -d '{"title":"test","content":"content"}'
|
||||
curl -X GET /api/read_note/test
|
||||
curl -X GET /api/search_notes?q=content
|
||||
# Measure: response times, error rates, data consistency
|
||||
```
|
||||
|
||||
#### Phase 2: Database Integration Testing
|
||||
3. **Turso Integration Test**:
|
||||
```bash
|
||||
# Configure Turso connection in container
|
||||
export DATABASE_URL="libsql://test-db.turso.io?authToken=..."
|
||||
|
||||
# Test all MCP tools with remote database
|
||||
basic-memory tools # Test each tool functionality
|
||||
# Test API write → manual sync workflow
|
||||
```
|
||||
|
||||
#### Phase 3: Production Readiness Testing
|
||||
4. **Performance Benchmarking**:
|
||||
- Container startup time with TigrisFS mounting
|
||||
- API operation response times (target: <500ms for typical operations)
|
||||
- Search query performance with Turso (target: comparable to local SQLite)
|
||||
- TigrisFS cache hit rates and memory usage
|
||||
- Concurrent tenant isolation
|
||||
|
||||
#### Phase 4: Local Access Testing (If Phase 1-3 Succeed)
|
||||
5. **Local Access Validation**:
|
||||
```bash
|
||||
# Test read-only access
|
||||
tigrisfs tenant-bucket ~/local-tenant
|
||||
ls -la ~/local-tenant # Browse files
|
||||
cp ~/local-tenant/notes/* ~/backup/ # Copy files
|
||||
|
||||
# Test file watching (stretch goal)
|
||||
echo "test" > ~/local-tenant/test.md
|
||||
# Check if changes sync to cloud
|
||||
```
|
||||
|
||||
### Go/No-Go Criteria by Phase
|
||||
- **Phase 1**: Container must start successfully and serve API requests over TigrisFS
|
||||
- **Phase 2**: All MCP tools must work with Turso with <2x latency increase
|
||||
- **Phase 3**: Performance must be within 50% of current Fly volume setup
|
||||
- **Phase 4**: Local mounting must work reliably for read-only access
|
||||
|
||||
### Risk Assessment
|
||||
**Moderate Risk Items (Mitigated by API-First Architecture)**:
|
||||
- [ ] TigrisFS performance for local access may have higher latency than local filesystem
|
||||
- [ ] File watching (`inotify`) over FUSE may be unreliable for local development
|
||||
- [ ] Network interruptions could cause filesystem errors during local editing
|
||||
- [ ] Write-back caching could cause data loss if container crashes during flush
|
||||
|
||||
**Low Risk Items (API-First Eliminates)**:
|
||||
- [ ] ~~Real-time file watching~~ - Not required for cloud operations
|
||||
- [ ] ~~Concurrent write consistency~~ - Single-tenant model with API coordination
|
||||
- [ ] ~~S3 rate limits~~ - TigrisFS intelligent caching handles this
|
||||
|
||||
**Mitigation Strategies**:
|
||||
- **Performance**: Comprehensive benchmarking with realistic workloads
|
||||
- **Reliability**: Graceful degradation to read-only local access if live editing fails
|
||||
- **Data Safety**: Regular sync intervals and write-through mode for critical operations
|
||||
- **Fallback**: Keep Fly volumes as backup deployment option
|
||||
|
||||
### Metrics to Track
|
||||
- **API Latency**: Response times for MCP tools and web operations
|
||||
- **Cache Effectiveness**: TigrisFS cache hit rates and memory usage
|
||||
- **Local Access Performance**: File browsing and copying speeds
|
||||
- **Reliability**: Success rate of mount operations and data consistency
|
||||
- **Cost**: Storage usage, API calls, and network transfer costs vs current volumes
|
||||
|
||||
## Notes
|
||||
|
||||
### Key Architectural Decisions
|
||||
- **Single tenant per bucket/database**: Simplifies isolation and credential management
|
||||
- **Maintain POSIX compatibility**: Preserve Basic Memory's existing filesystem assumptions
|
||||
- **TigrisFS over rclone**: Purpose-built for object storage with intelligent caching
|
||||
- **Turso for SQLite**: Leverages specialized remote SQLite expertise
|
||||
- **API-first approach**: Eliminates file watching dependency for cloud operations
|
||||
|
||||
### Alternative Approaches Considered
|
||||
- **S3-native storage backend**: Would require Basic Memory architecture changes
|
||||
- **Hybrid approach**: Local files + cloud sync (adds complexity)
|
||||
- **Standard rclone mounting**: Less optimized than TigrisFS for object storage workloads
|
||||
- **Keep Fly volumes**: Maintains current limitations but proven reliability
|
||||
|
||||
### Integration Points
|
||||
- [ ] Fly.io Tigris integration for bucket provisioning
|
||||
- [ ] Turso account setup and database provisioning
|
||||
- [ ] Container image modifications for TigrisFS support
|
||||
- [ ] Credential management for tenant isolation
|
||||
- [ ] API modification for manual sync triggers
|
||||
- [ ] Local client setup documentation for TigrisFS mounting
|
||||
|
||||
## Observations
|
||||
|
||||
- [architecture] Tigris/Turso split cleanly separates file storage from indexing concerns #storage-separation
|
||||
- [breakthrough] API-first architecture eliminates file watching dependency for cloud operations #api-first-advantage
|
||||
- [user-experience] Local mounting of cloud files could be revolutionary for knowledge management #local-cloud-hybrid
|
||||
- [compatibility] Maintaining POSIX filesystem assumptions preserves Basic Memory's local/cloud compatibility #architecture-preservation
|
||||
- [simplification] Single tenant per bucket eliminates complex multi-tenancy in storage layer #tenant-isolation
|
||||
- [performance] TigrisFS intelligent caching could provide near-local performance for common operations #tigrisfs-advantage
|
||||
- [deployment] Zero-downtime updates become trivial without volume constraints #deployment-simplification
|
||||
- [benefit] Object storage pricing model could be more favorable than volume pricing #cost-optimization
|
||||
- [innovation] Read-only local access alone would address major SaaS limitation #competitive-advantage
|
||||
- [risk-mitigation] API-driven sync reduces performance requirements vs real-time file watching #risk-reduction
|
||||
|
||||
## Relations
|
||||
|
||||
- implements [[SPEC-6 Explicit Project Parameter Architecture]]
|
||||
- requires [[Fly.io Tigris Integration]]
|
||||
- enables [[Local Cloud File Access]]
|
||||
- alternative_to [[Fly Volume Storage]]
|
||||
|
||||
## Links
|
||||
- https://fly.io/hello/tigris
|
||||
- https://fly.io/docs/tigris/
|
||||
- https://www.tigrisdata.com/docs/sdks/fly/data-migration-with-flyctl/
|
||||
- https://www.tigrisdata.com/docs/training/tigrisfs/
|
||||
- https://www.tigrisdata.com/blog/tigris-filesystem/
|
||||
- https://www.tigrisdata.com/docs/quickstarts/rclone/
|
||||
@@ -0,0 +1,886 @@
|
||||
---
|
||||
title: 'SPEC-8: TigrisFS Integration for Tenant API'
|
||||
Date: September 22, 2025
|
||||
Status: Phase 3.6 Complete - Tenant Mount API Endpoints Ready for CLI Implementation
|
||||
Priority: High
|
||||
Goal: Replace Fly volumes with Tigris bucket provisioning in production tenant API
|
||||
permalink: spec-8-tigris-fs-integration
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Based on SPEC-7 Phase 4 POC testing, this spec outlines productizing the TigrisFS/rclone implementation in the Basic Memory Cloud tenant API.
|
||||
We're moving from proof-of-concept to production integration, replacing Fly volume storage with Tigris bucket-per-tenant architecture.
|
||||
|
||||
## Current Architecture (Fly Volumes)
|
||||
|
||||
### Tenant Provisioning Flow
|
||||
```python
|
||||
# apps/cloud/src/basic_memory_cloud/workflows/tenant_provisioning.py
|
||||
async def provision_tenant_infrastructure(tenant_id: str):
|
||||
# 1. Create Fly app
|
||||
# 2. Create Fly volume ← REPLACE THIS
|
||||
# 3. Deploy API container with volume mount
|
||||
# 4. Configure health checks
|
||||
```
|
||||
|
||||
### Storage Implementation
|
||||
- Each tenant gets dedicated Fly volume (1GB-10GB)
|
||||
- Volume mounted at `/app/data` in API container
|
||||
- Local filesystem storage with Basic Memory indexing
|
||||
- No global caching or edge distribution
|
||||
|
||||
## Proposed Architecture (Tigris Buckets)
|
||||
|
||||
### New Tenant Provisioning Flow
|
||||
```python
|
||||
async def provision_tenant_infrastructure(tenant_id: str):
|
||||
# 1. Create Fly app
|
||||
# 2. Create Tigris bucket with admin credentials ← NEW
|
||||
# 3. Store bucket name in tenant record ← NEW
|
||||
# 4. Deploy API container with TigrisFS mount using admin credentials
|
||||
# 5. Configure health checks
|
||||
```
|
||||
|
||||
### Storage Implementation
|
||||
- Each tenant gets dedicated Tigris bucket
|
||||
- TigrisFS mounts bucket at `/app/data` in API container
|
||||
- Global edge caching and distribution
|
||||
- Configurable cache TTL for sync performance
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Bucket Provisioning Service
|
||||
|
||||
**✅ IMPLEMENTED: StorageClient with Admin Credentials**
|
||||
```python
|
||||
# apps/cloud/src/basic_memory_cloud/clients/storage_client.py
|
||||
class StorageClient:
|
||||
async def create_tenant_bucket(self, tenant_id: UUID) -> TigrisBucketCredentials
|
||||
async def delete_tenant_bucket(self, tenant_id: UUID, bucket_name: str) -> bool
|
||||
async def list_buckets(self) -> list[TigrisBucketResponse]
|
||||
async def test_tenant_credentials(self, credentials: TigrisBucketCredentials) -> bool
|
||||
```
|
||||
|
||||
**Simplified Architecture Using Admin Credentials:**
|
||||
- Single admin access key with full Tigris permissions (configured in console)
|
||||
- No tenant-specific IAM user creation needed
|
||||
- Bucket-per-tenant isolation for logical separation
|
||||
- Admin credentials shared across all tenant operations
|
||||
|
||||
**Integrate with Provisioning workflow:**
|
||||
```python
|
||||
# Update tenant_provisioning.py
|
||||
async def provision_tenant_infrastructure(tenant_id: str):
|
||||
storage_client = StorageClient(settings.aws_access_key_id, settings.aws_secret_access_key)
|
||||
bucket_creds = await storage_client.create_tenant_bucket(tenant_id)
|
||||
await store_bucket_name(tenant_id, bucket_creds.bucket_name)
|
||||
await deploy_api_with_tigris(tenant_id, bucket_creds)
|
||||
```
|
||||
|
||||
### Phase 2: Simplified Bucket Management
|
||||
|
||||
**✅ SIMPLIFIED: Admin Credentials + Bucket Names Only**
|
||||
|
||||
Since we use admin credentials for all operations, we only need to track bucket names per tenant:
|
||||
|
||||
1. **Primary Storage (Fly Secrets)**
|
||||
```bash
|
||||
flyctl secrets set -a basic-memory-{tenant_id} \
|
||||
AWS_ACCESS_KEY_ID="{admin_access_key}" \
|
||||
AWS_SECRET_ACCESS_KEY="{admin_secret_key}" \
|
||||
AWS_ENDPOINT_URL_S3="https://fly.storage.tigris.dev" \
|
||||
AWS_REGION="auto" \
|
||||
BUCKET_NAME="basic-memory-{tenant_id}"
|
||||
```
|
||||
|
||||
2. **Database Storage (Bucket Name Only)**
|
||||
```python
|
||||
# apps/cloud/src/basic_memory_cloud/models/tenant.py
|
||||
class Tenant(BaseModel):
|
||||
# ... existing fields
|
||||
tigris_bucket_name: Optional[str] = None # Just store bucket name
|
||||
tigris_region: str = "auto"
|
||||
created_at: datetime
|
||||
```
|
||||
|
||||
**Benefits of Simplified Approach:**
|
||||
- No credential encryption/decryption needed
|
||||
- Admin credentials managed centrally in environment
|
||||
- Only bucket names stored in database (not sensitive)
|
||||
- Simplified backup/restore scenarios
|
||||
- Reduced security attack surface
|
||||
|
||||
### Phase 3: API Container Updates
|
||||
|
||||
**Update API container configuration:**
|
||||
```dockerfile
|
||||
# apps/api/Dockerfile
|
||||
# Add TigrisFS installation
|
||||
RUN curl -L https://github.com/tigrisdata/tigrisfs/releases/latest/download/tigrisfs-linux-amd64 \
|
||||
-o /usr/local/bin/tigrisfs && chmod +x /usr/local/bin/tigrisfs
|
||||
```
|
||||
|
||||
**Startup script integration:**
|
||||
```bash
|
||||
# apps/api/tigrisfs-startup.sh (already exists)
|
||||
# Mount TigrisFS → Start Basic Memory API
|
||||
exec python -m basic_memory_cloud_api.main
|
||||
```
|
||||
|
||||
**Fly.toml environment (optimized for < 5s startup):**
|
||||
```toml
|
||||
# apps/api/fly.tigris-production.toml
|
||||
[env]
|
||||
TIGRISFS_MEMORY_LIMIT = '1024' # Reduced for faster init
|
||||
TIGRISFS_MAX_FLUSHERS = '16' # Fewer threads for faster startup
|
||||
TIGRISFS_STAT_CACHE_TTL = '30s' # Balance sync speed vs startup
|
||||
TIGRISFS_LAZY_INIT = 'true' # Enable lazy loading
|
||||
BASIC_MEMORY_HOME = '/app/data'
|
||||
|
||||
# Suspend optimization for wake-on-network
|
||||
[machine]
|
||||
auto_stop_machines = "suspend" # Faster than full stop
|
||||
auto_start_machines = true
|
||||
min_machines_running = 0
|
||||
```
|
||||
|
||||
### Phase 4: Local Access Features
|
||||
|
||||
**CLI automation for local mounting:**
|
||||
```python
|
||||
# New CLI command: basic-memory cloud mount
|
||||
async def setup_local_mount(tenant_id: str):
|
||||
# 1. Fetch bucket credentials from cloud API
|
||||
# 2. Configure rclone with scoped IAM policy
|
||||
# 3. Mount via rclone nfsmount (macOS) or FUSE (Linux)
|
||||
# 4. Start Basic Memory sync watcher
|
||||
```
|
||||
|
||||
**Local mount configuration:**
|
||||
```bash
|
||||
# rclone config for tenant
|
||||
rclone mount basic-memory-{tenant_id}: ~/basic-memory-{tenant_id} \
|
||||
--nfs-mount \
|
||||
--vfs-cache-mode writes \
|
||||
--cache-dir ~/.cache/rclone/basic-memory-{tenant_id}
|
||||
```
|
||||
|
||||
### Phase 5: TigrisFS Cache Sync Solutions
|
||||
|
||||
**Problem**: When files are uploaded via CLI/bisync, the tenant API container doesn't see them immediately due to TigrisFS cache (30s TTL) and lack of inotify events on mounted filesystems.
|
||||
|
||||
**Multi-Layer Solution:**
|
||||
|
||||
**Layer 1: API Sync Endpoint** (Immediate)
|
||||
```python
|
||||
# POST /sync - Force TigrisFS cache refresh
|
||||
# Callable by CLI after uploads
|
||||
subprocess.run(["sync", "fsync /app/data"], check=True)
|
||||
```
|
||||
|
||||
**Layer 2: Tigris Webhook Integration** (Real-time)
|
||||
https://www.tigrisdata.com/docs/buckets/object-notifications/#webhook
|
||||
```python
|
||||
# Webhook endpoint for bucket changes
|
||||
@app.post("/webhooks/tigris/{tenant_id}")
|
||||
async def handle_bucket_notification(tenant_id: str, event: TigrisEvent):
|
||||
if event.eventName in ["OBJECT_CREATED_PUT", "OBJECT_DELETED"]:
|
||||
await notify_container_sync(tenant_id, event.object.key)
|
||||
```
|
||||
|
||||
**Layer 3: CLI Sync Notification** (User-triggered)
|
||||
```bash
|
||||
# CLI calls container sync endpoint after successful bisync
|
||||
basic-memory cloud bisync # Automatically notifies container
|
||||
curl -X POST https://basic-memory-{tenant-id}.fly.dev/sync
|
||||
```
|
||||
|
||||
**Layer 4: Periodic Sync Fallback** (Safety net)
|
||||
```python
|
||||
# Background task: fsync /app/data every 30s as fallback
|
||||
# Ensures eventual consistency even if other layers fail
|
||||
```
|
||||
|
||||
**Implementation Priority:**
|
||||
1. Layer 1 (API endpoint) - Quick testing capability
|
||||
2. Layer 3 (CLI integration) - Improved UX
|
||||
3. Layer 4 (Periodic fallback) - Safety net
|
||||
4. Layer 2 (Webhooks) - Production real-time sync
|
||||
|
||||
|
||||
## Performance Targets
|
||||
|
||||
### Sync Latency
|
||||
- **Target**: < 5 seconds local→cloud→container
|
||||
- **Configuration**: `TIGRISFS_STAT_CACHE_TTL = '5s'`
|
||||
- **Monitoring**: Track sync metrics in production
|
||||
|
||||
### Container Startup
|
||||
- **Target**: < 5 seconds including TigrisFS mount
|
||||
- **Fast retry**: 0.5s intervals for mount verification
|
||||
- **Fallback**: Container fails fast if mount fails
|
||||
|
||||
### Memory Usage
|
||||
- **TigrisFS cache**: 2GB memory limit per container
|
||||
- **Concurrent uploads**: 32 flushers max
|
||||
- **VM sizing**: shared-cpu-2x (2048mb) minimum
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Bucket Isolation
|
||||
- Each tenant has dedicated bucket
|
||||
- IAM policies prevent cross-tenant access
|
||||
- No shared bucket with subdirectories
|
||||
|
||||
### Credential Security
|
||||
- Fly secrets for runtime access
|
||||
- Encrypted database backup for disaster recovery
|
||||
- Credential rotation capability
|
||||
|
||||
### Data Residency
|
||||
- Tigris global edge caching
|
||||
- SOC2 Type II compliance
|
||||
- Encryption at rest and in transit
|
||||
|
||||
## Operational Benefits
|
||||
|
||||
### Scalability
|
||||
- Horizontal scaling with stateless API containers
|
||||
- Global edge distribution
|
||||
- Better resource utilization
|
||||
|
||||
### Reliability
|
||||
- No cold starts between tenants
|
||||
- Built-in redundancy and caching
|
||||
- Simplified backup strategy
|
||||
|
||||
### Cost Efficiency
|
||||
- Pay-per-use storage pricing
|
||||
- Shared infrastructure benefits
|
||||
- Reduced operational overhead
|
||||
|
||||
## Risk Mitigation
|
||||
|
||||
### Data Loss Prevention
|
||||
- Dual credential storage (Fly + database)
|
||||
- Automated backup workflows to R2/S3
|
||||
- Tigris built-in redundancy
|
||||
|
||||
### Performance Degradation
|
||||
- Configurable cache settings per tenant
|
||||
- Monitoring and alerting on sync latency
|
||||
- Fallback to volume storage if needed
|
||||
|
||||
### Security Vulnerabilities
|
||||
- Bucket-per-tenant isolation
|
||||
- Regular credential rotation
|
||||
- Security scanning and monitoring
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Technical Metrics
|
||||
- Sync latency P50 < 5 seconds
|
||||
- Container startup time < 5 seconds
|
||||
- Zero data loss incidents
|
||||
- 99.9% uptime per tenant
|
||||
|
||||
### Business Metrics
|
||||
- Reduced infrastructure costs vs volumes
|
||||
- Improved user experience with faster sync
|
||||
- Enhanced enterprise security posture
|
||||
- Simplified operational overhead
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Tigris rate limits**: What are the API limits for bucket creation?
|
||||
2. **Cost analysis**: What's the break-even point vs Fly volumes?
|
||||
3. **Regional preferences**: Should enterprise customers choose regions?
|
||||
4. **Backup retention**: How long to keep automated backups?
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
### Phase 1: Bucket Provisioning Service ✅ COMPLETED
|
||||
- [x] **Research Tigris bucket API** - Document bucket creation and S3 API compatibility
|
||||
- [x] **Create StorageClient class** - Implemented with admin credentials and comprehensive integration tests
|
||||
- [x] **Test bucket creation** - Full test suite validates API integration with real Tigris environment
|
||||
- [x] **Add bucket provisioning to DBOS workflow** - Integrated StorageClient with tenant_provisioning.py
|
||||
|
||||
### Phase 2: Simplified Bucket Management ✅ COMPLETED
|
||||
- [x] **Update Tenant model** with tigris_bucket_name field (replaced fly_volume_id)
|
||||
- [x] **Implement bucket name storage** - Database migration and model updates completed
|
||||
- [x] **Test bucket provisioning integration** - Full test suite validates workflow from tenant creation to bucket assignment
|
||||
- [x] **Remove volume logic from all tests** - Complete migration from volume-based to bucket-based architecture
|
||||
|
||||
### Phase 3: API Container Integration ✅ COMPLETED
|
||||
- [x] **Update Dockerfile** to install TigrisFS binary in API container with configurable version
|
||||
- [x] **Optimize tigrisfs-startup.sh** with production-ready security and reliability improvements
|
||||
- [x] **Create production-ready container** with proper signal handling and mount validation
|
||||
- [x] **Implement security fixes** based on Claude code review (conditional debug, credential protection)
|
||||
- [x] **Add proper process supervision** with cleanup traps and error handling
|
||||
- [x] **Remove debug artifacts** - Cleaned up all debug Dockerfiles and test scripts
|
||||
|
||||
### Phase 3.5: IAM Access Key Management ✅ COMPLETED
|
||||
- [x] **Research Tigris IAM API** - Documented create_policy, attach_user_policy, delete_access_key operations
|
||||
- [x] **Implement bucket-scoped credential generation** - StorageClient.create_tenant_access_keys() with IAM policies
|
||||
- [x] **Add comprehensive security test suite** - 5 security-focused integration tests covering all attack vectors
|
||||
- [x] **Verify cross-bucket access prevention** - Scoped credentials can ONLY access their designated bucket
|
||||
- [x] **Test credential lifecycle management** - Create, validate, delete, and revoke access keys
|
||||
- [x] **Validate admin vs scoped credential isolation** - Different access patterns and security boundaries
|
||||
- [x] **Test multi-tenant isolation** - Multiple tenants cannot access each other's buckets
|
||||
|
||||
### Phase 3.6: Tenant Mount API Endpoints ✅ COMPLETED
|
||||
- [x] **Implement GET /tenant/mount/info** - Returns mount info without exposing credentials
|
||||
- [x] **Implement POST /tenant/mount/credentials** - Creates new bucket-scoped credentials for CLI mounting
|
||||
- [x] **Implement DELETE /tenant/mount/credentials/{cred_id}** - Revoke specific credentials with proper cleanup
|
||||
- [x] **Implement GET /tenant/mount/credentials** - List active credentials without exposing secrets
|
||||
- [x] **Add TenantMountCredentials database model** - Tracks credential metadata (no secret storage)
|
||||
- [x] **Create comprehensive test suite** - 28 tests covering all scenarios including multi-session support
|
||||
- [x] **Implement multi-session credential flow** - Multiple active credentials per tenant supported
|
||||
- [x] **Secure credential handling** - Secret keys never stored, returned once only for immediate use
|
||||
- [x] **Add dependency injection for StorageClient** - Clean integration with existing API architecture
|
||||
- [x] **Fix Tigris configuration for cloud service** - Added AWS environment variables to fly.template.toml
|
||||
- [x] **Update tenant machine configurations** - Include AWS credentials for TigrisFS mounting with clear credential strategy
|
||||
|
||||
**Security Test Results:**
|
||||
```
|
||||
✅ Cross-bucket access prevention - PASS
|
||||
✅ Deleted credentials access revoked - PASS
|
||||
✅ Invalid credentials rejected - PASS
|
||||
✅ Admin vs scoped credential isolation - PASS
|
||||
✅ Multiple scoped credentials isolation - PASS
|
||||
```
|
||||
|
||||
**Implementation Details:**
|
||||
- Uses Tigris IAM managed policies (create_policy + attach_user_policy)
|
||||
- Bucket-scoped S3 policies with Actions: GetObject, PutObject, DeleteObject, ListBucket
|
||||
- Resource ARNs limited to specific bucket: `arn:aws:s3:::bucket-name` and `arn:aws:s3:::bucket-name/*`
|
||||
- Access keys follow Tigris format: `tid_` prefix with secure random suffix
|
||||
- Complete cleanup on deletion removes both access keys and associated policies
|
||||
|
||||
### Phase 4: Local Access CLI
|
||||
- [x] **Design local mount CLI command** for automated rclone configuration
|
||||
- [x] **Implement credential fetching** from cloud API for local setup
|
||||
- [x] **Create rclone config automation** for tenant-specific bucket mounting
|
||||
- [x] **Test local→cloud→container sync** with optimized cache settings
|
||||
- [x] **Document local access setup** for beta users
|
||||
|
||||
### Phase 5: Webhook Integration (Future)
|
||||
- [ ] **Research Tigris webhook API** for object notifications and payload format
|
||||
- [ ] **Design webhook endpoint** for real-time sync notifications
|
||||
- [ ] **Implement notification handling** to trigger Basic Memory sync events
|
||||
- [ ] **Test webhook delivery** and sync latency improvements
|
||||
|
||||
## Success Metrics
|
||||
- [ ] **Container startup < 5 seconds** including TigrisFS mount and Basic Memory init
|
||||
- [ ] **Sync latency < 5 seconds** for local→cloud→container file changes
|
||||
- [ ] **Zero data loss** during bucket provisioning and credential management
|
||||
- [ ] **100% test coverage** for new TigrisBucketService and credential functions
|
||||
- [ ] **Beta deployment** with internal users validating local-cloud workflow
|
||||
|
||||
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
## Phase 4.1: Bidirectional Sync with rclone bisync (NEW)
|
||||
|
||||
### Problem Statement
|
||||
During testing, we discovered that some applications (particularly Obsidian) don't detect file changes over NFS mounts. Rather than building a custom sync daemon, we can leverage `rclone bisync` - rclone's built-in bidirectional synchronization feature.
|
||||
|
||||
### Solution: rclone bisync
|
||||
Use rclone's proven bidirectional sync instead of custom implementation:
|
||||
|
||||
**Core Architecture:**
|
||||
```bash
|
||||
# rclone bisync handles all the complexity
|
||||
rclone bisync ~/basic-memory-{tenant_id} basic-memory-{tenant_id}:{bucket_name} \
|
||||
--create-empty-src-dirs \
|
||||
--conflict-resolve newer \
|
||||
--resilient \
|
||||
--check-access
|
||||
```
|
||||
|
||||
**Key Benefits:**
|
||||
- ✅ **Battle-tested**: Production-proven rclone functionality
|
||||
- ✅ **MIT licensed**: Open source with permissive licensing
|
||||
- ✅ **No custom code**: Zero maintenance burden for sync logic
|
||||
- ✅ **Built-in safety**: max-delete protection, conflict resolution
|
||||
- ✅ **Simple installation**: Works with Homebrew rclone (no FUSE needed)
|
||||
- ✅ **File watcher compatible**: Works with Obsidian and all applications
|
||||
- ✅ **Offline support**: Can work offline and sync when connected
|
||||
|
||||
### bisync Conflict Resolution Options
|
||||
|
||||
**Built-in conflict strategies:**
|
||||
```bash
|
||||
--conflict-resolve none # Keep both files with .conflict suffixes (safest)
|
||||
--conflict-resolve newer # Always pick the most recently modified file
|
||||
--conflict-resolve larger # Choose based on file size
|
||||
--conflict-resolve path1 # Always prefer local changes
|
||||
--conflict-resolve path2 # Always prefer cloud changes
|
||||
```
|
||||
|
||||
### Sync Profiles Using bisync
|
||||
|
||||
**Profile configurations:**
|
||||
```python
|
||||
BISYNC_PROFILES = {
|
||||
"safe": {
|
||||
"conflict_resolve": "none", # Keep both versions
|
||||
"max_delete": 10, # Prevent mass deletion
|
||||
"check_access": True, # Verify sync integrity
|
||||
"description": "Safe mode with conflict preservation"
|
||||
},
|
||||
"balanced": {
|
||||
"conflict_resolve": "newer", # Auto-resolve to newer file
|
||||
"max_delete": 25,
|
||||
"check_access": True,
|
||||
"description": "Balanced mode (recommended default)"
|
||||
},
|
||||
"fast": {
|
||||
"conflict_resolve": "newer",
|
||||
"max_delete": 50,
|
||||
"check_access": False, # Skip verification for speed
|
||||
"description": "Fast mode for rapid iteration"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CLI Commands
|
||||
|
||||
**Manual sync commands:**
|
||||
```bash
|
||||
basic-memory cloud bisync # Manual bidirectional sync
|
||||
basic-memory cloud bisync --dry-run # Preview changes
|
||||
basic-memory cloud bisync --profile safe # Use specific profile
|
||||
basic-memory cloud bisync --resync # Force full baseline resync
|
||||
```
|
||||
|
||||
**Watch mode (Step 1):**
|
||||
```bash
|
||||
basic-memory cloud bisync --watch # Long-running process, sync every 60s
|
||||
basic-memory cloud bisync --watch --interval 30s # Custom interval
|
||||
```
|
||||
|
||||
**System integration (Step 2 - Future):**
|
||||
```bash
|
||||
basic-memory cloud bisync-service install # Install as system service
|
||||
basic-memory cloud bisync-service start # Start background service
|
||||
basic-memory cloud bisync-service status # Check service status
|
||||
```
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
**Phase 4.1.1: Core bisync Implementation**
|
||||
- [ ] Implement `run_bisync()` function wrapping rclone bisync
|
||||
- [ ] Add profile-based configuration (safe/balanced/fast)
|
||||
- [ ] Create conflict resolution and safety options
|
||||
- [ ] Test with sample files and conflict scenarios
|
||||
|
||||
**Phase 4.1.2: Watch Mode**
|
||||
- [ ] Add `--watch` flag for continuous sync
|
||||
- [ ] Implement configurable sync intervals
|
||||
- [ ] Add graceful shutdown and signal handling
|
||||
- [ ] Create status monitoring and progress indicators
|
||||
|
||||
**Phase 4.1.3: User Experience**
|
||||
- [ ] Add conflict reporting and resolution guidance
|
||||
- [ ] Implement dry-run preview functionality
|
||||
- [ ] Create troubleshooting and diagnostic commands
|
||||
- [ ] Add filtering configuration (.gitignore-style)
|
||||
|
||||
**Phase 4.1.4: System Integration (Future)**
|
||||
- [ ] Generate platform-specific service files (launchd/systemd)
|
||||
- [ ] Add service management commands
|
||||
- [ ] Implement automatic startup and recovery
|
||||
- [ ] Create monitoring and logging integration
|
||||
|
||||
### Technical Implementation
|
||||
|
||||
**Core bisync wrapper:**
|
||||
```python
|
||||
def run_bisync(
|
||||
tenant_id: str,
|
||||
bucket_name: str,
|
||||
profile: str = "balanced",
|
||||
dry_run: bool = False
|
||||
) -> bool:
|
||||
"""Run rclone bisync with specified profile."""
|
||||
|
||||
local_path = Path.home() / f"basic-memory-{tenant_id}"
|
||||
remote_path = f"basic-memory-{tenant_id}:{bucket_name}"
|
||||
profile_config = BISYNC_PROFILES[profile]
|
||||
|
||||
cmd = [
|
||||
"rclone", "bisync",
|
||||
str(local_path), remote_path,
|
||||
"--create-empty-src-dirs",
|
||||
"--resilient",
|
||||
f"--conflict-resolve={profile_config['conflict_resolve']}",
|
||||
f"--max-delete={profile_config['max_delete']}",
|
||||
"--filters-file", "~/.basic-memory/bisync-filters.txt"
|
||||
]
|
||||
|
||||
if profile_config.get("check_access"):
|
||||
cmd.append("--check-access")
|
||||
|
||||
if dry_run:
|
||||
cmd.append("--dry-run")
|
||||
|
||||
return subprocess.run(cmd, check=True).returncode == 0
|
||||
```
|
||||
|
||||
**Default filter file (~/.basic-memory/bisync-filters.txt):**
|
||||
```
|
||||
- .DS_Store
|
||||
- .git/**
|
||||
- __pycache__/**
|
||||
- *.pyc
|
||||
- .pytest_cache/**
|
||||
- node_modules/**
|
||||
- .conflict-*
|
||||
- Thumbs.db
|
||||
- desktop.ini
|
||||
```
|
||||
|
||||
**Advantages Over Custom Daemon:**
|
||||
- ✅ **Zero maintenance**: No custom sync logic to debug/maintain
|
||||
- ✅ **Production proven**: Used by thousands in production
|
||||
- ✅ **Safety features**: Built-in max-delete, conflict handling, recovery
|
||||
- ✅ **Filtering**: Advanced exclude patterns and rules
|
||||
- ✅ **Performance**: Optimized for various storage backends
|
||||
- ✅ **Community support**: Extensive documentation and community
|
||||
|
||||
## Phase 4.2: NFS Mount Support (Direct Access)
|
||||
|
||||
### Solution: rclone nfsmount
|
||||
Keep the existing NFS mount functionality for users who prefer direct file access:
|
||||
|
||||
**Core Architecture:**
|
||||
```bash
|
||||
# rclone nfsmount provides transparent file access
|
||||
rclone nfsmount basic-memory-{tenant_id}:{bucket_name} ~/basic-memory-{tenant_id} \
|
||||
--vfs-cache-mode writes \
|
||||
--dir-cache-time 10s \
|
||||
--daemon
|
||||
```
|
||||
|
||||
**Key Benefits:**
|
||||
- ✅ **Real-time access**: Files appear immediately as they're created/modified
|
||||
- ✅ **Transparent**: Works with any application that reads/writes files
|
||||
- ✅ **Low latency**: Direct access without sync delays
|
||||
- ✅ **Simple**: No periodic sync commands needed
|
||||
- ✅ **Homebrew compatible**: Works with Homebrew rclone (no FUSE required)
|
||||
|
||||
**Limitations:**
|
||||
- ❌ **File watcher compatibility**: Some apps (Obsidian) don't detect changes over NFS
|
||||
- ❌ **Network dependency**: Requires active connection to cloud storage
|
||||
- ❌ **Potential conflicts**: Simultaneous edits from multiple locations can cause issues
|
||||
|
||||
### Mount Profiles (Existing)
|
||||
|
||||
**Already implemented profiles from SPEC-7 testing:**
|
||||
```python
|
||||
MOUNT_PROFILES = {
|
||||
"fast": {
|
||||
"cache_time": "5s",
|
||||
"poll_interval": "3s",
|
||||
"description": "Ultra-fast development (5s sync)"
|
||||
},
|
||||
"balanced": {
|
||||
"cache_time": "10s",
|
||||
"poll_interval": "5s",
|
||||
"description": "Fast development (10-15s sync, recommended)"
|
||||
},
|
||||
"safe": {
|
||||
"cache_time": "15s",
|
||||
"poll_interval": "10s",
|
||||
"description": "Conflict-aware mount with backup",
|
||||
"extra_args": ["--conflict-suffix", ".conflict-{DateTimeExt}"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CLI Commands (Existing)
|
||||
|
||||
**Mount commands already implemented:**
|
||||
```bash
|
||||
basic-memory cloud mount # Mount with balanced profile
|
||||
basic-memory cloud mount --profile fast # Ultra-fast caching
|
||||
basic-memory cloud mount --profile safe # Conflict detection
|
||||
basic-memory cloud unmount # Clean unmount
|
||||
basic-memory cloud mount-status # Show mount status
|
||||
```
|
||||
|
||||
## User Choice: Mount vs Bisync
|
||||
|
||||
### When to Use Each Approach
|
||||
|
||||
| Use Case | Recommended Solution | Why |
|
||||
|----------|---------------------|-----|
|
||||
| **Obsidian users** | `bisync` | File watcher support for live preview |
|
||||
| **CLI/vim/emacs users** | `mount` | Direct file access, lower latency |
|
||||
| **Offline work** | `bisync` | Can work offline, sync when connected |
|
||||
| **Real-time collaboration** | `mount` | Immediate visibility of changes |
|
||||
| **Multiple machines** | `bisync` | Better conflict handling |
|
||||
| **Single machine** | `mount` | Simpler, more transparent |
|
||||
| **Development work** | Either | Both work well, user preference |
|
||||
| **Large files** | `mount` | Streaming access vs full download |
|
||||
|
||||
### Installation Simplicity
|
||||
|
||||
**Both approaches now use simple Homebrew installation:**
|
||||
```bash
|
||||
# Single installation command for both approaches
|
||||
brew install rclone
|
||||
|
||||
# No macFUSE, no system modifications needed
|
||||
# Works immediately with both mount and bisync
|
||||
```
|
||||
|
||||
### Implementation Status
|
||||
|
||||
**Phase 4.1: bisync** (NEW)
|
||||
- [ ] Implement bisync command wrapper
|
||||
- [ ] Add watch mode with configurable intervals
|
||||
- [ ] Create conflict resolution workflows
|
||||
- [ ] Add filtering and safety options
|
||||
|
||||
**Phase 4.2: mount** (EXISTING - ✅ IMPLEMENTED)
|
||||
- [x] NFS mount commands with profile support
|
||||
- [x] Mount management and cleanup
|
||||
- [x] Process monitoring and health checks
|
||||
- [x] Credential integration with cloud API
|
||||
|
||||
**Both approaches share:**
|
||||
- [x] Credential management via cloud API
|
||||
- [x] Secure rclone configuration
|
||||
- [x] Tenant isolation and bucket scoping
|
||||
- [x] Simple Homebrew rclone installation
|
||||
|
||||
|
||||
Key Features:
|
||||
|
||||
1. Cross-Platform rclone Installation (rclone_installer.py):
|
||||
- macOS: Homebrew → official script fallback
|
||||
- Linux: snap → apt → official script fallback
|
||||
- Windows: winget → chocolatey → scoop fallback
|
||||
- Automatic version detection and verification
|
||||
|
||||
2. Smart rclone Configuration (rclone_config.py):
|
||||
- Automatic tenant-specific config generation
|
||||
- Three optimized mount profiles from your SPEC-7 testing:
|
||||
- fast: 5s sync (ultra-performance)
|
||||
- balanced: 10-15s sync (recommended default)
|
||||
- safe: 15s sync + conflict detection
|
||||
- Backup existing configs before modification
|
||||
|
||||
3. Robust Mount Management (mount_commands.py):
|
||||
- Automatic tenant credential generation
|
||||
- Mount path management (~/basic-memory-{tenant-id})
|
||||
- Process lifecycle management (prevent duplicate mounts)
|
||||
- Orphaned process cleanup
|
||||
- Mount verification and health checking
|
||||
|
||||
4. Clean Architecture (api_client.py):
|
||||
- Separated API client to avoid circular imports
|
||||
- Reuses existing authentication infrastructure
|
||||
- Consistent error handling and logging
|
||||
|
||||
User Experience:
|
||||
|
||||
One-Command Setup:
|
||||
basic-memory cloud setup
|
||||
```bash
|
||||
# 1. Installs rclone automatically
|
||||
# 2. Authenticates with existing login
|
||||
# 3. Generates secure credentials
|
||||
# 4. Configures rclone
|
||||
# 5. Performs initial mount
|
||||
```
|
||||
|
||||
Profile-Based Mounting:
|
||||
basic-memory cloud mount --profile fast # 5s sync
|
||||
basic-memory cloud mount --profile balanced # 15s sync (default)
|
||||
basic-memory cloud mount --profile safe # conflict detection
|
||||
|
||||
Status Monitoring:
|
||||
basic-memory cloud mount-status
|
||||
```bash
|
||||
# Shows: tenant info, mount path, sync profile, rclone processes
|
||||
```
|
||||
### local mount api
|
||||
|
||||
Endpoint 1: Get Tenant Info for user
|
||||
Purpose: Get tenant details for mounting
|
||||
- pass in jwt
|
||||
- service returns mount info
|
||||
|
||||
**✅ IMPLEMENTED API Specification:**
|
||||
|
||||
**Endpoint 1: GET /tenant/mount/info**
|
||||
- Purpose: Get tenant mount information without exposing credentials
|
||||
- Authentication: JWT token (tenant_id extracted from claims)
|
||||
|
||||
Request:
|
||||
```
|
||||
GET /tenant/mount/info
|
||||
Authorization: Bearer {jwt_token}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"tenant_id": "434252dd-d83b-4b20-bf70-8a950ff875c4",
|
||||
"bucket_name": "basic-memory-434252dd",
|
||||
"has_credentials": true,
|
||||
"credentials_created_at": "2025-09-22T16:48:50.414694"
|
||||
}
|
||||
```
|
||||
|
||||
**Endpoint 2: POST /tenant/mount/credentials**
|
||||
- Purpose: Generate NEW bucket-scoped S3 credentials for rclone mounting
|
||||
- Authentication: JWT token (tenant_id extracted from claims)
|
||||
- Multi-session: Creates new credentials without revoking existing ones
|
||||
|
||||
Request:
|
||||
```
|
||||
POST /tenant/mount/credentials
|
||||
Authorization: Bearer {jwt_token}
|
||||
Content-Type: application/json
|
||||
```
|
||||
*Note: No request body needed - tenant_id extracted from JWT*
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"tenant_id": "434252dd-d83b-4b20-bf70-8a950ff875c4",
|
||||
"bucket_name": "basic-memory-434252dd",
|
||||
"access_key": "test_access_key_12345",
|
||||
"secret_key": "test_secret_key_abcdef",
|
||||
"endpoint_url": "https://fly.storage.tigris.dev",
|
||||
"region": "auto"
|
||||
}
|
||||
```
|
||||
|
||||
**🔒 Security Notes:**
|
||||
- Secret key returned ONCE only - never stored in database
|
||||
- Credentials are bucket-scoped (cannot access other tenants' buckets)
|
||||
- Multiple active credentials supported per tenant (work laptop + personal machine)
|
||||
|
||||
Implementation Notes
|
||||
|
||||
Security:
|
||||
- Both endpoints require JWT authentication
|
||||
- Extract tenant_id from JWT claims (not request body)
|
||||
- Generate scoped credentials (not admin credentials)
|
||||
- Credentials should have bucket-specific access only
|
||||
|
||||
Integration Points:
|
||||
- Use your existing StorageClient from SPEC-8 implementation
|
||||
- Leverage existing JWT middleware for tenant extraction
|
||||
- Return same credential format as your Tigris bucket provisioning
|
||||
|
||||
Error Handling:
|
||||
- 401 if not authenticated
|
||||
- 403 if tenant doesn't exist
|
||||
- 500 if credential generation fails
|
||||
|
||||
**🔄 Design Decisions:**
|
||||
|
||||
1. **Secure Credential Flow (No Secret Storage)**
|
||||
|
||||
Based on CLI flow analysis, we follow security best practices:
|
||||
- ✅ API generates both access_key + secret_key via Tigris IAM
|
||||
- ✅ Returns both in API response for immediate use
|
||||
- ✅ CLI uses credentials immediately to configure rclone
|
||||
- ✅ Database stores only metadata (access_key + policy_arn for cleanup)
|
||||
- ✅ rclone handles secure local credential storage
|
||||
- ❌ **Never store secret_key in database (even encrypted)**
|
||||
|
||||
2. **CLI Credential Flow**
|
||||
```bash
|
||||
# CLI calls API
|
||||
POST /tenant/mount/credentials → {access_key, secret_key, ...}
|
||||
|
||||
# CLI immediately configures rclone
|
||||
rclone config create basic-memory-{tenant_id} s3 \
|
||||
access_key_id={access_key} \
|
||||
secret_access_key={secret_key} \
|
||||
endpoint=https://fly.storage.tigris.dev
|
||||
|
||||
# Database tracks metadata only
|
||||
INSERT INTO tenant_mount_credentials (tenant_id, access_key, policy_arn, ...)
|
||||
```
|
||||
|
||||
3. **Multiple Sessions Supported**
|
||||
|
||||
- Users can have multiple active credential sets (work laptop, personal machine, etc.)
|
||||
- Each credential generation creates a new Tigris access key
|
||||
- List active credentials via API (shows access_key but never secret)
|
||||
|
||||
4. **Failure Handling & Cleanup**
|
||||
|
||||
- **Happy Path**: Credentials created → Used immediately → rclone configured
|
||||
- **Orphaned Credentials**: Background job revokes unused credentials
|
||||
- **API Failure Recovery**: Retry Tigris deletion with stored policy_arn
|
||||
- **Status Tracking**: Track tigris_deletion_status (pending/completed/failed)
|
||||
|
||||
5. **Event Sourcing & Audit**
|
||||
|
||||
- MountCredentialCreatedEvent
|
||||
- MountCredentialRevokedEvent
|
||||
- MountCredentialOrphanedEvent (for cleanup)
|
||||
- Full audit trail for security compliance
|
||||
|
||||
6. **Tenant/Bucket Validation**
|
||||
|
||||
- Verify tenant exists and has valid bucket before credential generation
|
||||
- Use existing StorageClient to validate bucket access
|
||||
- Prevent credential generation for inactive/invalid tenants
|
||||
|
||||
📋 **Implemented API Endpoints:**
|
||||
|
||||
```
|
||||
✅ IMPLEMENTED:
|
||||
GET /tenant/mount/info # Get tenant/bucket info (no credentials exposed)
|
||||
POST /tenant/mount/credentials # Generate new credentials (returns secret once)
|
||||
GET /tenant/mount/credentials # List active credentials (no secrets)
|
||||
DELETE /tenant/mount/credentials/{cred_id} # Revoke specific credentials
|
||||
```
|
||||
|
||||
**API Implementation Status:**
|
||||
- ✅ **GET /tenant/mount/info**: Returns tenant_id, bucket_name, has_credentials, credentials_created_at
|
||||
- ✅ **POST /tenant/mount/credentials**: Creates new bucket-scoped access keys, returns access_key + secret_key once
|
||||
- ✅ **GET /tenant/mount/credentials**: Lists active credentials without exposing secret keys
|
||||
- ✅ **DELETE /tenant/mount/credentials/{cred_id}**: Revokes specific credentials with proper Tigris IAM cleanup
|
||||
- ✅ **Multi-session support**: Multiple active credentials per tenant (work laptop + personal machine)
|
||||
- ✅ **Security**: Secret keys never stored in database, returned once only for immediate use
|
||||
- ✅ **Comprehensive test suite**: 28 tests covering all scenarios including error handling and multi-session flows
|
||||
- ✅ **Dependency injection**: Clean integration with existing FastAPI architecture
|
||||
- ✅ **Production-ready configuration**: Tigris credentials properly configured for tenant machines
|
||||
|
||||
🗄️ **Secure Database Schema:**
|
||||
|
||||
```sql
|
||||
CREATE TABLE tenant_mount_credentials (
|
||||
id UUID PRIMARY KEY,
|
||||
tenant_id UUID REFERENCES tenant(id),
|
||||
access_key VARCHAR(255) NOT NULL,
|
||||
-- secret_key REMOVED - never store secrets (security best practice)
|
||||
policy_arn VARCHAR(255) NOT NULL, -- For Tigris IAM cleanup
|
||||
tigris_deletion_status VARCHAR(20) DEFAULT 'pending', -- Track cleanup
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
revoked_at TIMESTAMP NULL,
|
||||
last_used_at TIMESTAMP NULL, -- Track usage for orphan cleanup
|
||||
description VARCHAR(255) DEFAULT 'CLI mount credentials'
|
||||
);
|
||||
```
|
||||
|
||||
**Security Benefits:**
|
||||
- ✅ Database breach cannot expose secrets
|
||||
- ✅ Follows "secrets don't persist" security principle
|
||||
- ✅ Meets compliance requirements (SOC2, etc.)
|
||||
- ✅ Reduced attack surface
|
||||
- ✅ CLI gets credentials once and stores securely via rclone
|
||||