     1→# MSP Mode Specification
     2→
     3→**Date Started:** 2026-01-15
     4→**Status:** Planning / Design Phase
     5→
     6→---
     7→
     8→## Overview
     9→
    10→Creating a custom "MSP Mode" for Claude Code that tracks client work, maintains context across sessions and machines, and provides structured access to historical MSP data.
    11→
    12→## Objectives
    13→
    14→1. **Track MSP client work** - Sessions, work items, time, credentials
    15→2. **Multi-machine access** - Same context available on all machines
    16→3. **Long-term context** - Search historical work across clients/projects
    17→4. **Scalable & robust** - Designed to evolve over time
    18→
    19→---
    20→
    21→## Architecture Decisions
    22→
    23→### Storage: SQL Database (MariaDB)
    24→
    25→**Decision:** Use SQL database instead of local files + Git sync
    26→
    27→**Rationale:**
    28→- Claude Code requires internet anyway (offline not a real advantage)
    29→- Structured queries needed ("show all work for Client X in January")
    30→- Relational data (clients → projects → sessions → credentials → billing)
    31→- Fast indexing and search even with years of data
    32→- No merge conflicts (single source of truth)
    33→- Time tracking and billing calculations
    34→- Report generation capabilities
    35→
    36→**Infrastructure:**
    37→- Existing MariaDB on Jupiter (172.16.3.20)
    38→- New database: `msp_tracking`
    39→
    40→### Access Method: REST API with JWT Authentication
    41→
    42→**Decision:** FastAPI REST API on Jupiter with JWT tokens
    43→
    44→**Rationale - Security:**
    45→- Token-based auth (revocable, rotatable)
    46→- Scoped permissions (API can't access other databases)
    47→- Audit logging (track all queries by user/token/timestamp)
    48→- Rate limiting possible
    49→- HTTPS encryption in transit
    50→- Token revocation without DB password changes
    51→- IP restrictions and 2FA possible later
    52→
    53→**Rationale - Scalability:**
    54→- Industry standard approach
    55→- Can add team members later
    56→- Other tools can integrate (scripts, mobile, GuruRMM)
    57→- Stateless authentication (no session storage)
    58→- Multiple clients supported
    59→
    60→**Rationale - Robustness:**
    61→- Comprehensive error handling
    62→- Input validation before DB access
    63→- Structured logging
    64→- Health checks and monitoring
    65→- Version controlled schema migrations
    66→
    67→### Technology Stack
    68→
    69→**API Framework: FastAPI (Python)**
    70→- Async performance for concurrent requests
    71→- Auto-generated OpenAPI/Swagger documentation
    72→- Type safety with Pydantic models (runtime validation)
    73→- SQLAlchemy ORM for complex queries
    74→- Built-in background tasks
    75→- Industry-standard testing with pytest
    76→- Alembic for database migrations
    77→- Mature dependency injection
    78→
    79→**Authentication: JWT Tokens**
    80→- Stateless (no DB lookup to validate)
    81→- Claims-based (permissions, scopes, expiration)
    82→- Refresh token pattern for long-term access
    83→- Multiple clients/machines supported
    84→- Short-lived tokens minimize compromise risk
    85→- Industry standard
    86→
    87→**Configuration Storage: Gitea (Private Repo)**
    88→- Multi-machine sync
    89→- Version controlled
    90→- Single source of truth
    91→- Token rotation = one commit, all machines sync
    92→- Encrypted token values (git-crypt or encrypted JSON)
    93→- Backup via Gitea
    94→
    95→**Deployment: Docker Container**
    96→- Easy deployment and updates
    97→- Resource limits
    98→- Systemd service for auto-restart
    99→- Portable (can migrate to dedicated host later)
   100→
   101→---
   102→
   103→## Infrastructure Design
   104→
   105→### Jupiter Server (172.16.3.20)
   106→
   107→**Docker Container: msp-api**
   108→- FastAPI application (Python 3.11+)
   109→- SQLAlchemy + Alembic (ORM and migrations)
   110→- JWT auth library (python-jose)
   111→- Pydantic validation
   112→- Gunicorn/Uvicorn ASGI server
   113→- Health checks endpoint
   114→- Prometheus metrics (optional)
   115→- Mounted logs: /var/log/msp-api/
   116→
   117→**MariaDB Database: msp_tracking**
   118→- Connection pooling (SQLAlchemy)
   119→- Automated backups (critical MSP data)
   120→- Schema versioned with Alembic
   121→
   122→**Nginx Reverse Proxy**
   123→- HTTPS with Let's Encrypt
   124→- Rate limiting
   125→- Access logs
   126→- Proxies to: msp-api.azcomputerguru.com
   127→
   128→### Gitea Private Repository
   129→
   130→**Repo: azcomputerguru/claude-settings (or new msp-config repo)**
   131→
   132→Structure:
   133→```
   134→msp-api-config.json
   135→├── api_url (https://msp-api.azcomputerguru.com)
   136→├── api_token (encrypted JWT or refresh token)
   137→└── database_schema_version (for migration tracking)
   138→```
   139→
   140→### Local Machine (D:\ClaudeTools)
   141→
   142→**Directory Structure:**
   143→```
   144→D:\ClaudeTools\
   145→├── .claude/
   146→│   ├── commands/
   147→│   │   ├── msp.md (MSP Mode slash command)
   148→│   │   ├── dev.md (Development Mode - TBD)
   149→│   │   └── normal.md (Normal Mode - TBD)
   150→│   └── msp-api-config.json (synced from Gitea)
   151→├── MSP-MODE-SPEC.md (this file)
   152→└── .git/ (synced to Gitea)
   153→```
   154→
   155→---
   156→
   157→## API Design Principles
   158→
   159→### Versioning
   160→- Start with `/api/v1/` from day one
   161→- Allows breaking changes in future versions
   162→
   163→### Security
   164→- All endpoints require JWT authentication
   165→- Input validation with Pydantic models
   166→- Never expose database errors to client
   167→- Rate limiting to prevent abuse
   168→- Comprehensive audit logging
   169→
   170→### Endpoints (Draft - To Be Detailed)
   171→```
   172→POST   /api/v1/sessions          (start new MSP session)
   173→GET    /api/v1/sessions          (query sessions - filters: client, date range, etc.)
   174→POST   /api/v1/work-items        (log work performed)
   175→GET    /api/v1/clients           (list clients)
   176→POST   /api/v1/clients           (create client record)
   177→GET    /api/v1/clients/{id}/credentials
   178→POST   /api/v1/auth/token        (get JWT token)
   179→POST   /api/v1/auth/refresh      (refresh expired token)
   180→GET    /api/v1/health            (health check)
   181→GET    /api/v1/metrics           (Prometheus metrics - optional)
   182→```
   183→
   184→### Error Handling
   185→- Structured JSON error responses
   186→- HTTP status codes (400, 401, 403, 404, 429, 500)
   187→- Never leak sensitive information in errors
   188→- Log all errors with context
   189→
   190→### Logging
   191→- JSON structured logs (easy parsing)
   192→- Log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
   193→- Include: timestamp, user/token ID, endpoint, duration, status
   194→- Separate audit log for sensitive operations (credential access, deletions)
   195→
   196→---
   197→
   198→## JWT Token Structure
   199→
   200→### Access Token (Short-lived: 1 hour)
   201→```json
   202→{
   203→  "sub": "mike@azcomputerguru.com",
   204→  "scopes": ["msp:read", "msp:write", "msp:admin"],
   205→  "machine": "windows-workstation",
   206→  "exp": 1234567890,
   207→  "iat": 1234567890,
   208→  "jti": "unique-token-id"
   209→}
   210→```
   211→
   212→### Refresh Token (Long-lived: 30 days)
   213→- Stored securely in Gitea config
   214→- Used to obtain new access tokens
   215→- Can be revoked server-side
   216→
   217→### Scopes (Permissions)
   218→- `msp:read` - Read sessions, clients, work items
   219→- `msp:write` - Create/update sessions, work items
   220→- `msp:admin` - Manage clients, credentials, delete operations
   221→
   222→---
   223→
   224→## Database Schema (Draft - To Be Detailed)
   225→
   226→### Tables (High-Level)
   227→```
   228→clients
   229→- id, name, network_cidr, primary_contact, notes, created_at, updated_at
   230→
   231→projects
   232→- id, client_id, name, description, status, created_at, updated_at
   233→
   234→sessions
   235→- id, project_id, start_time, end_time, billable_hours, notes, created_at
   236→
   237→work_items
   238→- id, session_id, description, category, timestamp, billable, created_at
   239→
   240→credentials
   241→- id, client_id, service, username, password_encrypted, notes, created_at, updated_at
   242→
   243→tags (for categorization)
   244→- id, name, type (client_tag, project_tag, work_tag)
   245→
   246→session_tags (many-to-many)
   247→- session_id, tag_id
   248→```
   249→
   250→### Schema Versioning
   251→- Alembic migrations in version control
   252→- Schema version tracked in config
   253→- Automated migration on API startup (optional)
   254→
   255→---
   256→
   257→## Modes Overview (D:\ClaudeTools Context)
   258→
   259→### 1. MSP Mode
   260→- **Purpose:** Track client work, maintain context across sessions
   261→- **Activation:** `/msp` slash command
   262→- **Behaviors:** (To Be Detailed)
   263→  - Prompt for client/project at start
   264→  - Auto-log work items as we work
   265→  - Track time spent
   266→  - Save credentials securely
   267→  - Generate session summary at end
   268→
   269→### 2. Development Mode
   270→- **Purpose:** (To Be Detailed)
   271→- **Activation:** `/dev` slash command
   272→- **Behaviors:** (To Be Detailed)
   273→
   274→### 3. Normal Mode
   275→- **Purpose:** Return to standard Claude behavior
   276→- **Activation:** `/normal` slash command
   277→- **Behaviors:** (To Be Detailed)
   278→  - Clear active mode context
   279→  - Standard conversational Claude
   280→
   281→---
   282→
   283→## Database Schema Design
   284→
   285→**Status:** ✅ Analyzed via 5 parallel agents on 2026-01-15
   286→
   287→Based on comprehensive analysis of:
   288→- 37 session logs (Dec 2025 - Jan 2026)
   289→- shared-data/credentials.md
   290→- All project directories and documentation
   291→- Infrastructure and client network patterns
   292→
   293→### Schema Summary
   294→
   295→**Total Tables:** 25 core tables + 5 junction tables = **30 tables**
   296→
   297→**Categories:**
   298→1. Core MSP Tracking (5 tables)
   299→2. Client & Infrastructure (7 tables)
   300→3. Credentials & Security (4 tables)
   301→4. Work Details (6 tables)
   302→5. Tagging & Categorization (3 tables)
   303→6. System & Audit (2 tables)
   304→7. **External Integrations (3 tables)** - Added 2026-01-15
   305→
   306→---
   307→
   308→### 1. Core MSP Tracking Tables (5 tables)
   309→
   310→#### `clients`
   311→Master table for all client organizations.
   312→
   313→```sql
   314→CREATE TABLE clients (
   315→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   316→    name VARCHAR(255) NOT NULL UNIQUE,
   317→    type VARCHAR(50) NOT NULL CHECK(type IN ('msp_client', 'internal', 'project')),
   318→    network_subnet VARCHAR(100), -- e.g., "192.168.0.0/24"
   319→    domain_name VARCHAR(255), -- AD domain or primary domain
   320→    m365_tenant_id UUID, -- Microsoft 365 tenant ID
   321→    primary_contact VARCHAR(255),
   322→    notes TEXT,
   323→    is_active BOOLEAN DEFAULT true,
   324→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   325→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   326→
   327→    INDEX idx_clients_type (type),
   328→    INDEX idx_clients_name (name)
   329→);
   330→```
   331→
   332→**Examples:** Dataforth, Grabb & Durando, Valley Wide Plastering, AZ Computer Guru (internal)
   333→
   334→---
   335→
   336→#### `projects`
   337→Individual projects/engagements for clients.
   338→
   339→```sql
   340→CREATE TABLE projects (
   341→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   342→    client_id UUID NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
   343→    name VARCHAR(255) NOT NULL,
   344→    slug VARCHAR(255) UNIQUE, -- directory name: "dataforth-dos"
   345→    category VARCHAR(50) CHECK(category IN (
   346→        'client_project', 'internal_product', 'infrastructure',
   347→        'website', 'development_tool', 'documentation'
   348→    )),
   349→    status VARCHAR(50) DEFAULT 'working' CHECK(status IN (
   350→        'complete', 'working', 'blocked', 'pending', 'critical', 'deferred'
   351→    )),
   352→    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
   353→    description TEXT,
   354→    started_date DATE,
   355→    target_completion_date DATE,
   356→    completed_date DATE,
   357→    estimated_hours DECIMAL(10,2),
   358→    actual_hours DECIMAL(10,2),
   359→    gitea_repo_url VARCHAR(500),
   360→    notes TEXT,
   361→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   362→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   363→
   364→    INDEX idx_projects_client (client_id),
   365→    INDEX idx_projects_status (status),
   366→    INDEX idx_projects_slug (slug)
   367→);
   368→```
   369→
   370→**Examples:** dataforth-dos, gururmm, grabb-website-move
   371→
   372→---
   373→
   374→#### `sessions`
   375→Work sessions with time tracking.
   376→
   377→```sql
   378→CREATE TABLE sessions (
   379→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   380→    client_id UUID REFERENCES clients(id) ON DELETE SET NULL,
   381→    project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
   382→    session_date DATE NOT NULL,
   383→    start_time TIMESTAMP,
   384→    end_time TIMESTAMP,
   385→    duration_minutes INTEGER, -- auto-calculated or manual
   386→    status VARCHAR(50) DEFAULT 'completed' CHECK(status IN (
   387→        'completed', 'in_progress', 'blocked', 'pending'
   388→    )),
   389→    session_title VARCHAR(500) NOT NULL,
   390→    summary TEXT, -- markdown summary
   391→    is_billable BOOLEAN DEFAULT false,
   392→    billable_hours DECIMAL(10,2),
   393→    technician VARCHAR(255), -- "Mike Swanson", etc.
   394→    session_log_file VARCHAR(500), -- path to .md file
   395→    notes TEXT,
   396→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   397→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   398→
   399→    INDEX idx_sessions_client (client_id),
   400→    INDEX idx_sessions_project (project_id),
   401→    INDEX idx_sessions_date (session_date),
   402→    INDEX idx_sessions_billable (is_billable)
   403→);
   404→```
   405→
   406→---
   407→
   408→#### `work_items`
   409→Individual tasks/actions within sessions (granular tracking).
   410→
   411→```sql
   412→CREATE TABLE work_items (
   413→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   414→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   415→    category VARCHAR(50) NOT NULL CHECK(category IN (
   416→        'infrastructure', 'troubleshooting', 'configuration',
   417→        'development', 'maintenance', 'security', 'documentation'
   418→    )),
   419→    title VARCHAR(500) NOT NULL,
   420→    description TEXT NOT NULL,
   421→    status VARCHAR(50) DEFAULT 'completed' CHECK(status IN (
   422→        'completed', 'in_progress', 'blocked', 'pending', 'deferred'
   423→    )),
   424→    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
   425→    is_billable BOOLEAN DEFAULT false,
   426→    estimated_minutes INTEGER,
   427→    actual_minutes INTEGER,
   428→    affected_systems TEXT, -- JSON array: ["jupiter", "172.16.3.20"]
   429→    technologies_used TEXT, -- JSON array: ["docker", "mariadb"]
   430→    item_order INTEGER, -- sequence within session
   431→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   432→    completed_at TIMESTAMP,
   433→
   434→    INDEX idx_work_items_session (session_id),
   435→    INDEX idx_work_items_category (category),
   436→    INDEX idx_work_items_status (status)
   437→);
   438→```
   439→
   440→**Categories distribution (from analysis):**
   441→- Infrastructure: 30%
   442→- Troubleshooting: 25%
   443→- Configuration: 15%
   444→- Development: 15%
   445→- Maintenance: 10%
   446→- Security: 5%
   447→
   448→---
   449→
   450→#### `pending_tasks`
   451→Open items across all clients/projects.
   452→
   453→```sql
   454→CREATE TABLE pending_tasks (
   455→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   456→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   457→    project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
   458→    work_item_id UUID REFERENCES work_items(id) ON DELETE SET NULL,
   459→    title VARCHAR(500) NOT NULL,
   460→    description TEXT,
   461→    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
   462→    blocked_by TEXT, -- what's blocking this
   463→    assigned_to VARCHAR(255),
   464→    due_date DATE,
   465→    status VARCHAR(50) DEFAULT 'pending' CHECK(status IN (
   466→        'pending', 'in_progress', 'blocked', 'completed', 'cancelled'
   467→    )),
   468→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   469→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   470→    completed_at TIMESTAMP,
   471→
   472→    INDEX idx_pending_tasks_client (client_id),
   473→    INDEX idx_pending_tasks_status (status),
   474→    INDEX idx_pending_tasks_priority (priority)
   475→);
   476→```
   477→
   478→---
   479→
   480→### 2. Client & Infrastructure Tables (7 tables)
   481→
   482→#### `sites`
   483→Physical/logical locations for clients.
   484→
   485→```sql
   486→CREATE TABLE sites (
   487→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   488→    client_id UUID NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
   489→    name VARCHAR(255) NOT NULL, -- "Main Office", "SLC - Salt Lake City"
   490→    network_subnet VARCHAR(100), -- "172.16.9.0/24"
   491→    vpn_required BOOLEAN DEFAULT false,
   492→    vpn_subnet VARCHAR(100), -- "192.168.1.0/24"
   493→    gateway_ip VARCHAR(45), -- IPv4/IPv6
   494→    dns_servers TEXT, -- JSON array
   495→    notes TEXT,
   496→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   497→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   498→
   499→    INDEX idx_sites_client (client_id)
   500→);
   501→```
   502→
   503→---
   504→
   505→#### `infrastructure`
   506→Servers, network devices, NAS, workstations.
   507→
   508→```sql
   509→CREATE TABLE infrastructure (
   510→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   511→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   512→    site_id UUID REFERENCES sites(id) ON DELETE SET NULL,
   513→    asset_type VARCHAR(50) NOT NULL CHECK(asset_type IN (
   514→        'physical_server', 'virtual_machine', 'container',
   515→        'network_device', 'nas_storage', 'workstation',
   516→        'firewall', 'domain_controller'
   517→    )),
   518→    hostname VARCHAR(255) NOT NULL,
   519→    ip_address VARCHAR(45),
   520→    mac_address VARCHAR(17),
   521→    os VARCHAR(255), -- "Ubuntu 22.04", "Windows Server 2022", "Unraid"
   522→    role_description TEXT, -- "Primary DC, NPS/RADIUS server"
   523→    parent_host_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL, -- for VMs/containers
   524→    status VARCHAR(50) DEFAULT 'active' CHECK(status IN (
   525→        'active', 'migration_source', 'migration_destination', 'decommissioned'
   526→    )),
   527→    notes TEXT,
   528→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   529→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   530→
   531→    INDEX idx_infrastructure_client (client_id),
   532→    INDEX idx_infrastructure_type (asset_type),
   533→    INDEX idx_infrastructure_hostname (hostname),
   534→    INDEX idx_infrastructure_parent (parent_host_id)
   535→);
   536→```
   537→
   538→**Examples:** Jupiter, AD2 (Dataforth), VWP-DC1, D2TESTNAS
   539→
   540→---
   541→
   542→#### `services`
   543→Applications/services running on infrastructure.
   544→
   545→```sql
   546→CREATE TABLE services (
   547→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   548→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
   549→    service_name VARCHAR(255) NOT NULL, -- "Gitea", "PostgreSQL", "Apache"
   550→    service_type VARCHAR(100), -- "git_hosting", "database", "web_server"
   551→    external_url VARCHAR(500), -- "https://git.azcomputerguru.com"
   552→    internal_url VARCHAR(500), -- "http://172.16.3.20:3000"
   553→    port INTEGER,
   554→    protocol VARCHAR(50), -- "https", "ssh", "smb"
   555→    status VARCHAR(50) DEFAULT 'running' CHECK(status IN (
   556→        'running', 'stopped', 'error', 'maintenance'
   557→    )),
   558→    version VARCHAR(100),
   559→    notes TEXT,
   560→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   561→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   562→
   563→    INDEX idx_services_infrastructure (infrastructure_id),
   564→    INDEX idx_services_name (service_name),
   565→    INDEX idx_services_type (service_type)
   566→);
   567→```
   568→
   569→---
   570→
   571→#### `service_relationships`
   572→Dependencies and relationships between services.
   573→
   574→```sql
   575→CREATE TABLE service_relationships (
   576→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   577→    from_service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
   578→    to_service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
   579→    relationship_type VARCHAR(50) NOT NULL CHECK(relationship_type IN (
   580→        'hosted_on', 'proxied_by', 'authenticates_via',
   581→        'backend_for', 'depends_on', 'replicates_to'
   582→    )),
   583→    notes TEXT,
   584→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   585→
   586→    UNIQUE(from_service_id, to_service_id, relationship_type),
   587→    INDEX idx_service_rel_from (from_service_id),
   588→    INDEX idx_service_rel_to (to_service_id)
   589→);
   590→```
   591→
   592→**Examples:**
   593→- Gitea (proxied_by) NPM
   594→- GuruRMM API (hosted_on) Jupiter container
   595→
   596→---
   597→
   598→#### `networks`
   599→Network segments, VLANs, VPN networks.
   600→
   601→```sql
   602→CREATE TABLE networks (
   603→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   604→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   605→    site_id UUID REFERENCES sites(id) ON DELETE CASCADE,
   606→    network_name VARCHAR(255) NOT NULL,
   607→    network_type VARCHAR(50) CHECK(network_type IN (
   608→        'lan', 'vpn', 'vlan', 'isolated', 'dmz'
   609→    )),
   610→    cidr VARCHAR(100) NOT NULL, -- "192.168.0.0/24"
   611→    gateway_ip VARCHAR(45),
   612→    vlan_id INTEGER,
   613→    notes TEXT,
   614→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   615→
   616→    INDEX idx_networks_client (client_id),
   617→    INDEX idx_networks_site (site_id)
   618→);
   619→```
   620→
   621→---
   622→
   623→#### `firewall_rules`
   624→Network security rules (for documentation/audit trail).
   625→
   626→```sql
   627→CREATE TABLE firewall_rules (
   628→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   629→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
   630→    rule_name VARCHAR(255),
   631→    source_cidr VARCHAR(100),
   632→    destination_cidr VARCHAR(100),
   633→    port INTEGER,
   634→    protocol VARCHAR(20), -- "tcp", "udp", "icmp"
   635→    action VARCHAR(20) CHECK(action IN ('allow', 'deny', 'drop')),
   636→    rule_order INTEGER,
   637→    notes TEXT,
   638→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   639→    created_by VARCHAR(255),
   640→
   641→    INDEX idx_firewall_infra (infrastructure_id)
   642→);
   643→```
   644→
   645→---
   646→
   647→#### `m365_tenants`
   648→Microsoft 365 tenant tracking.
   649→
   650→```sql
   651→CREATE TABLE m365_tenants (
   652→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   653→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   654→    tenant_id UUID NOT NULL UNIQUE, -- Microsoft tenant ID
   655→    tenant_name VARCHAR(255), -- "dataforth.com"
   656→    default_domain VARCHAR(255), -- "dataforthcorp.onmicrosoft.com"
   657→    admin_email VARCHAR(255),
   658→    cipp_name VARCHAR(255), -- name in CIPP portal
   659→    notes TEXT,
   660→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   661→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   662→
   663→    INDEX idx_m365_client (client_id),
   664→    INDEX idx_m365_tenant_id (tenant_id)
   665→);
   666→```
   667→
   668→---
   669→
   670→### 3. Credentials & Security Tables (4 tables)
   671→
   672→#### `credentials`
   673→Encrypted credential storage (values encrypted at rest).
   674→
   675→```sql
   676→CREATE TABLE credentials (
   677→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   678→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   679→    service_id UUID REFERENCES services(id) ON DELETE CASCADE,
   680→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
   681→    credential_type VARCHAR(50) NOT NULL CHECK(credential_type IN (
   682→        'password', 'api_key', 'oauth', 'ssh_key',
   683→        'shared_secret', 'jwt', 'connection_string', 'certificate'
   684→    )),
   685→    service_name VARCHAR(255) NOT NULL, -- "Gitea Admin", "AD2 sysadmin"
   686→    username VARCHAR(255),
   687→    password_encrypted BYTEA, -- AES-256-GCM encrypted
   688→    api_key_encrypted BYTEA,
   689→    client_id_oauth VARCHAR(255), -- for OAuth
   690→    client_secret_encrypted BYTEA,
   691→    tenant_id_oauth VARCHAR(255),
   692→    public_key TEXT, -- for SSH
   693→    token_encrypted BYTEA,
   694→    connection_string_encrypted BYTEA,
   695→    integration_code VARCHAR(255), -- for services like Autotask
   696→
   697→    -- Metadata
   698→    external_url VARCHAR(500),
   699→    internal_url VARCHAR(500),
   700→    custom_port INTEGER,
   701→    role_description VARCHAR(500),
   702→    requires_vpn BOOLEAN DEFAULT false,
   703→    requires_2fa BOOLEAN DEFAULT false,
   704→    ssh_key_auth_enabled BOOLEAN DEFAULT false,
   705→    access_level VARCHAR(100),
   706→
   707→    -- Lifecycle
   708→    expires_at TIMESTAMP,
   709→    last_rotated_at TIMESTAMP,
   710→    is_active BOOLEAN DEFAULT true,
   711→
   712→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   713→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   714→
   715→    INDEX idx_credentials_client (client_id),
   716→    INDEX idx_credentials_service (service_id),
   717→    INDEX idx_credentials_type (credential_type),
   718→    INDEX idx_credentials_active (is_active)
   719→);
   720→```
   721→
   722→**Security:**
   723→- All sensitive fields encrypted with AES-256-GCM
   724→- Encryption key stored separately (environment variable or vault)
   725→- Master password unlock mechanism
   726→
   727→---
   728→
   729→#### `credential_audit_log`
   730→Audit trail for credential access.
   731→
   732→```sql
   733→CREATE TABLE credential_audit_log (
   734→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   735→    credential_id UUID NOT NULL REFERENCES credentials(id) ON DELETE CASCADE,
   736→    action VARCHAR(50) NOT NULL CHECK(action IN (
   737→        'view', 'create', 'update', 'delete', 'rotate', 'decrypt'
   738→    )),
   739→    user_id VARCHAR(255) NOT NULL, -- JWT sub claim
   740→    ip_address VARCHAR(45),
   741→    user_agent TEXT,
   742→    details TEXT, -- JSON: what changed, why
   743→    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   744→
   745→    INDEX idx_cred_audit_credential (credential_id),
   746→    INDEX idx_cred_audit_user (user_id),
   747→    INDEX idx_cred_audit_timestamp (timestamp)
   748→);
   749→```
   750→
   751→---
   752→
   753→#### `security_incidents`
   754→Track security events and remediation.
   755→
   756→```sql
   757→CREATE TABLE security_incidents (
   758→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   759→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   760→    service_id UUID REFERENCES services(id) ON DELETE SET NULL,
   761→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
   762→    incident_type VARCHAR(100) CHECK(incident_type IN (
   763→        'bec', 'backdoor', 'malware', 'unauthorized_access',
   764→        'data_breach', 'phishing', 'ransomware', 'brute_force'
   765→    )),
   766→    incident_date TIMESTAMP NOT NULL,
   767→    severity VARCHAR(50) CHECK(severity IN ('critical', 'high', 'medium', 'low')),
   768→    description TEXT NOT NULL,
   769→    findings TEXT, -- investigation results
   770→    remediation_steps TEXT,
   771→    status VARCHAR(50) DEFAULT 'investigating' CHECK(status IN (
   772→        'investigating', 'contained', 'resolved', 'monitoring'
   773→    )),
   774→    resolved_at TIMESTAMP,
   775→    notes TEXT,
   776→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   777→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   778→
   779→    INDEX idx_incidents_client (client_id),
   780→    INDEX idx_incidents_type (incident_type),
   781→    INDEX idx_incidents_status (status)
   782→);
   783→```
   784→
   785→**Examples:** BG Builders OAuth backdoor, CW Concrete BEC
   786→
   787→---
   788→
   789→#### `credential_permissions`
   790→Access control for credentials (future team expansion).
   791→
   792→```sql
   793→CREATE TABLE credential_permissions (
   794→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   795→    credential_id UUID NOT NULL REFERENCES credentials(id) ON DELETE CASCADE,
   796→    user_id VARCHAR(255) NOT NULL, -- or role_id
   797→    permission_level VARCHAR(50) CHECK(permission_level IN ('read', 'write', 'admin')),
   798→    granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   799→    granted_by VARCHAR(255),
   800→
   801→    UNIQUE(credential_id, user_id),
   802→    INDEX idx_cred_perm_credential (credential_id),
   803→    INDEX idx_cred_perm_user (user_id)
   804→);
   805→```
   806→
   807→---
   808→
   809→### 4. Work Details Tables (6 tables)
   810→
   811→#### `file_changes`
   812→Track files created/modified/deleted during sessions.
   813→
   814→```sql
   815→CREATE TABLE file_changes (
   816→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   817→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   818→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   819→    file_path VARCHAR(1000) NOT NULL,
   820→    change_type VARCHAR(50) CHECK(change_type IN (
   821→        'created', 'modified', 'deleted', 'renamed', 'backed_up'
   822→    )),
   823→    backup_path VARCHAR(1000),
   824→    size_bytes BIGINT,
   825→    description TEXT,
   826→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   827→
   828→    INDEX idx_file_changes_work_item (work_item_id),
   829→    INDEX idx_file_changes_session (session_id)
   830→);
   831→```
   832→
   833→---
   834→
   835→#### `commands_run`
   836→Shell/PowerShell/SQL commands executed.
   837→
   838→```sql
   839→CREATE TABLE commands_run (
   840→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   841→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   842→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   843→    command_text TEXT NOT NULL,
   844→    host VARCHAR(255), -- where executed: "jupiter", "172.16.3.20"
   845→    shell_type VARCHAR(50), -- "bash", "powershell", "sql", "docker"
   846→    success BOOLEAN,
   847→    output_summary TEXT, -- first/last lines or error
   848→    execution_order INTEGER, -- sequence within work item
   849→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   850→
   851→    INDEX idx_commands_work_item (work_item_id),
   852→    INDEX idx_commands_session (session_id),
   853→    INDEX idx_commands_host (host)
   854→);
   855→```
   856→
   857→---
   858→
   859→#### `infrastructure_changes`
   860→Audit trail for infrastructure modifications.
   861→
   862→```sql
   863→CREATE TABLE infrastructure_changes (
   864→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   865→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   866→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   867→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
   868→    change_type VARCHAR(50) CHECK(change_type IN (
   869→        'dns', 'firewall', 'routing', 'ssl', 'container',
   870→        'service_config', 'hardware', 'network', 'storage'
   871→    )),
   872→    target_system VARCHAR(255) NOT NULL,
   873→    before_state TEXT,
   874→    after_state TEXT,
   875→    is_permanent BOOLEAN DEFAULT true,
   876→    rollback_procedure TEXT,
   877→    verification_performed BOOLEAN DEFAULT false,
   878→    verification_notes TEXT,
   879→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   880→
   881→    INDEX idx_infra_changes_work_item (work_item_id),
   882→    INDEX idx_infra_changes_session (session_id),
   883→    INDEX idx_infra_changes_infrastructure (infrastructure_id)
   884→);
   885→```
   886→
   887→---
   888→
   889→#### `problem_solutions`
   890→Issue tracking with root cause and resolution.
   891→
   892→```sql
   893→CREATE TABLE problem_solutions (
   894→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   895→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   896→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   897→    problem_description TEXT NOT NULL,
   898→    symptom TEXT, -- what user saw
   899→    error_message TEXT, -- exact error code/message
   900→    investigation_steps TEXT, -- JSON array of diagnostic commands
   901→    root_cause TEXT,
   902→    solution_applied TEXT NOT NULL,
   903→    verification_method TEXT,
   904→    rollback_plan TEXT,
   905→    recurrence_count INTEGER DEFAULT 1, -- if same problem reoccurs
   906→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   907→
   908→    INDEX idx_problems_work_item (work_item_id),
   909→    INDEX idx_problems_session (session_id)
   910→);
   911→```
   912→
   913→---
   914→
   915→#### `deployments`
   916→Track software/config deployments.
   917→
   918→```sql
   919→CREATE TABLE deployments (
   920→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   921→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   922→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   923→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
   924→    service_id UUID REFERENCES services(id) ON DELETE SET NULL,
   925→    deployment_type VARCHAR(50) CHECK(deployment_type IN (
   926→        'code', 'config', 'database', 'container', 'service_restart'
   927→    )),
   928→    version VARCHAR(100),
   929→    description TEXT,
   930→    deployed_from VARCHAR(500), -- source path or repo
   931→    deployed_to VARCHAR(500), -- destination
   932→    rollback_available BOOLEAN DEFAULT false,
   933→    rollback_procedure TEXT,
   934→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   935→
   936→    INDEX idx_deployments_work_item (work_item_id),
   937→    INDEX idx_deployments_infrastructure (infrastructure_id),
   938→    INDEX idx_deployments_service (service_id)
   939→);
   940→```
   941→
   942→---
   943→
   944→#### `database_changes`
   945→Track database schema/data modifications.
   946→
   947→```sql
   948→CREATE TABLE database_changes (
   949→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   950→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   951→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   952→    database_name VARCHAR(255) NOT NULL,
   953→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
   954→    change_type VARCHAR(50) CHECK(change_type IN (
   955→        'schema', 'data', 'index', 'optimization', 'cleanup', 'migration'
   956→    )),
   957→    sql_executed TEXT,
   958→    rows_affected BIGINT,
   959→    size_freed_bytes BIGINT, -- for cleanup operations
   960→    backup_taken BOOLEAN DEFAULT false,
   961→    backup_location VARCHAR(500),
   962→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   963→
   964→    INDEX idx_db_changes_work_item (work_item_id),
   965→    INDEX idx_db_changes_database (database_name)
   966→);
   967→```
   968→
   969→---
   970→
   971→### 5. Tagging & Categorization Tables (3 tables)
   972→
   973→#### `tags`
   974→Flexible tagging system for work items.
   975→
   976→```sql
   977→CREATE TABLE tags (
   978→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   979→    name VARCHAR(100) UNIQUE NOT NULL,
   980→    category VARCHAR(50) CHECK(category IN (
   981→        'technology', 'client', 'infrastructure',
   982→        'problem_type', 'action', 'service'
   983→    )),
   984→    description TEXT,
   985→    usage_count INTEGER DEFAULT 0, -- auto-increment on use
   986→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   987→
   988→    INDEX idx_tags_category (category),
   989→    INDEX idx_tags_name (name)
   990→);
   991→```
   992→
   993→**Pre-populated tags:** 157+ tags identified from analysis
   994→- 58 technology tags (docker, postgresql, apache, etc.)
   995→- 24 infrastructure tags (jupiter, saturn, pfsense, etc.)
   996→- 20+ client tags
   997→- 30 problem type tags (connection-timeout, ssl-error, etc.)
   998→- 25 action tags (migration, upgrade, cleanup, etc.)
   999→
  1000→---
  1001→
  1002→#### `work_item_tags` (Junction Table)
  1003→Many-to-many relationship: work items ↔ tags.
  1004→
  1005→```sql
  1006→CREATE TABLE work_item_tags (
  1007→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
  1008→    tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
  1009→    PRIMARY KEY (work_item_id, tag_id),
  1010→
  1011→    INDEX idx_wit_work_item (work_item_id),
  1012→    INDEX idx_wit_tag (tag_id)
  1013→);
  1014→```
  1015→
  1016→---
  1017→
  1018→#### `session_tags` (Junction Table)
  1019→Many-to-many relationship: sessions ↔ tags.
  1020→
  1021→```sql
  1022→CREATE TABLE session_tags (
  1023→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
  1024→    tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
  1025→    PRIMARY KEY (session_id, tag_id),
  1026→
  1027→    INDEX idx_st_session (session_id),
  1028→    INDEX idx_st_tag (tag_id)
  1029→);
  1030→```
  1031→
  1032→---
  1033→
  1034→### 6. System & Audit Tables (2 tables)
  1035→
  1036→#### `api_audit_log`
  1037→Track all API requests for security and debugging.
  1038→
  1039→```sql
  1040→CREATE TABLE api_audit_log (
  1041→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  1042→    user_id VARCHAR(255) NOT NULL, -- JWT sub claim
  1043→    endpoint VARCHAR(500) NOT NULL, -- "/api/v1/sessions"
  1044→    http_method VARCHAR(10), -- GET, POST, PUT, DELETE
  1045→    ip_address VARCHAR(45),
  1046→    user_agent TEXT,
  1047→    request_body TEXT, -- sanitized (no credentials)
  1048→    response_status INTEGER, -- 200, 401, 500
  1049→    response_time_ms INTEGER,
  1050→    error_message TEXT,
  1051→    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  1052→
  1053→    INDEX idx_api_audit_user (user_id),
  1054→    INDEX idx_api_audit_endpoint (endpoint),
  1055→    INDEX idx_api_audit_timestamp (timestamp),
  1056→    INDEX idx_api_audit_status (response_status)
  1057→);
  1058→```
  1059→
  1060→---
  1061→
  1062→#### `schema_migrations`
  1063→Track database schema versions (Alembic migrations).
  1064→
  1065→```sql
  1066→CREATE TABLE schema_migrations (
  1067→    version_id VARCHAR(100) PRIMARY KEY,
  1068→    description TEXT,
  1069→    applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  1070→    applied_by VARCHAR(255),
  1071→    migration_sql TEXT
  1072→);
  1073→```
  1074→
  1075→---
  1076→
  1077→### Junction Tables Summary
  1078→
  1079→1. **work_item_tags** - Work items ↔ Tags
  1080→2. **session_tags** - Sessions ↔ Tags
  1081→3. **project_relationships** (optional) - Projects ↔ Projects (related/dependent)
  1082→4. **project_session_logs** (optional) - Projects ↔ Sessions (many-to-many if sessions span multiple projects)
  1083→
  1084→---
  1085→
  1086→### Schema Statistics
  1087→
  1088→**Total Tables:** 30
  1089→- Core MSP: 5 tables
  1090→- Client & Infrastructure: 7 tables
  1091→- Credentials & Security: 4 tables
  1092→- Work Details: 6 tables
  1093→- Tagging: 3 tables (+ 2 junction)
  1094→- System: 2 tables
  1095→- External Integrations: 3 tables
  1096→
  1097→**Estimated Row Counts (1 year of MSP work):**
  1098→- sessions: ~500-1000 (2-3 per day)
  1099→- work_items: ~5,000-10,000 (5-10 per session)
  1100→- file_changes: ~10,000
  1101→- commands_run: ~20,000
  1102→- tags: ~200
  1103→- clients: ~50
  1104→- projects: ~100
  1105→- credentials: ~500
  1106→- api_audit_log: ~100,000+
  1107→
  1108→**Storage Estimate:** ~1-2 GB per year (compressed)
  1109→
  1110→---
  1111→
  1112→### Design Principles Applied
  1113→
  1114→1. **Normalized Structure** - Minimizes data duplication
  1115→2. **Flexible Tagging** - Supports evolving categorization
  1116→3. **Audit Trail** - Comprehensive logging for security and troubleshooting
  1117→4. **Scalability** - Designed for multi-user MSP team growth
  1118→5. **Security First** - Encrypted credentials, access control, audit logging
  1119→6. **Temporal Tracking** - created_at, updated_at, completed_at timestamps
  1120→7. **Soft Deletes** - is_active flags allow recovery
  1121→8. **Relationships** - Foreign keys enforce referential integrity
  1122→9. **Indexes** - Strategic indexes for common query patterns
  1123→10. **JSON Flexibility** - JSON fields for arrays/flexible data (affected_systems, technologies_used)
  1124→
  1125→---
  1126→
  1127→## Next Steps for Database Implementation
  1128→
  1129→1. ✅ Schema designed (27 tables, relationships defined)
  1130→2. ⏳ Create Alembic migration files
  1131→3. ⏳ Set up encryption key management
  1132→4. ⏳ Seed initial data (tags, MSP infrastructure)
  1133→5. ⏳ Create database on Jupiter MariaDB
  1134→6. ⏳ Build FastAPI models (SQLAlchemy + Pydantic)
  1135→7. ⏳ Implement API endpoints
  1136→8. ⏳ Create authentication flow
  1137→9. ⏳ Build MSP Mode slash command integration
  1138→
  1139→---
  1140→
  1141→## Open Questions
  1142→
  1143→### MSP Mode Behaviors (DEFINED)
  1144→
  1145→**Core Principle:** Automatically categorize client interactions and store useful data in brief but information-dense format.
  1146→
  1147→#### When `/msp` is Called (Session Start)
  1148→
  1149→1. **Client/Project Detection:**
  1150→   - Auto-detect from context if possible (mentions of client names, domains, IPs)
  1151→   - If ambiguous, present quick-select list of recent clients
  1152→   - Prompt: "Working on: [Client] - [Project]? (or select different)"
  1153→
  1154→2. **Session Initialization:**
  1155→   - Start timer automatically
  1156→   - Create session record with:
  1157→     - session_date (today)
  1158→     - start_time (now)
  1159→     - client_id, project_id (detected or selected)
  1160→     - status = 'in_progress'
  1161→
  1162→3. **Context Display:**
  1163→   - Show brief summary: "MSP Mode: [Client] - [Project] | Started: [time]"
  1164→   - Load relevant context: recent sessions, open tasks, credentials for this client
  1165→
  1166→#### During Session (Automatic Tracking)
  1167→
  1168→**Auto-Categorization - Work Items:**
  1169→As work progresses, Claude automatically creates work_items with:
  1170→
  1171→- **Category detection:**
  1172→  - Keywords trigger categories:
  1173→    - "ssh", "docker restart" → infrastructure
  1174→    - "error", "not working", "broken" → troubleshooting
  1175→    - "configure", "setup", "change settings" → configuration
  1176→    - "build", "code", "implement" → development
  1177→    - "cleanup", "optimize", "backup" → maintenance
  1178→    - "malware", "breach", "unauthorized" → security
  1179→
  1180→- **Technology tagging:**
  1181→  - Auto-detect from commands/context: docker, apache, mariadb, m365, etc.
  1182→  - Add to work_item.technologies_used array
  1183→
  1184→- **Affected systems:**
  1185→  - Extract IPs, hostnames from commands
  1186→  - Add to work_item.affected_systems array
  1187→
  1188→**Information-Dense Data Capture:**
  1189→
  1190→1. **Commands Run:**
  1191→   - Auto-log every bash/powershell/SQL command executed
  1192→   - Store: command_text, host, shell_type, success, output_summary (first/last 5 lines)
  1193→   - Link to current work_item
  1194→
  1195→2. **File Changes:**
  1196→   - Track when files are read/edited/written
  1197→   - Store: file_path, change_type, backup_path (if created), size_bytes
  1198→   - Brief description (auto-generated: "Modified Apache config for SSL")
  1199→
  1200→3. **Problems & Solutions:**
  1201→   - When user describes an error, auto-create problem_solution record:
  1202→     - symptom: "Users can't access website"
  1203→     - error_message: "ERR_CONNECTION_TIMED_OUT"
  1204→     - investigation_steps: [array of diagnostic commands]
  1205→     - root_cause: "Firewall blocking port 443"
  1206→     - solution_applied: "Added iptables ACCEPT rule for 443"
  1207→     - verification_method: "curl test successful"
  1208→
  1209→4. **Credentials Accessed:**
  1210→   - When retrieving credentials, log to credential_audit_log:
  1211→     - action: 'decrypt' or 'view'
  1212→     - credential_id
  1213→     - user_id (from JWT)
  1214→     - timestamp
  1215→   - Don't log the actual credential value (security)
  1216→
  1217→5. **Infrastructure Changes:**
  1218→   - Detect infrastructure modifications:
  1219→     - DNS changes → change_type: 'dns'
  1220→     - Firewall rules → change_type: 'firewall'
  1221→     - Service configs → change_type: 'service_config'
  1222→   - Store before_state, after_state, rollback_procedure
  1223→
  1224→**Concise Summaries:**
  1225→- Auto-generate brief descriptions:
  1226→  - Work item title: "Fixed Apache SSL certificate expiration on jupiter"
  1227→  - Problem description: "Website down: cert expired, renewed via certbot, verified"
  1228→  - Not verbose: avoid "I then proceeded to...", just facts
  1229→
  1230→#### Billability Detection
  1231→
  1232→**Auto-flag billable work:**
  1233→- Client work (non-internal) → is_billable = true by default
  1234→- Internal infrastructure → is_billable = false
  1235→- User can override with quick command: "/billable false"
  1236→
  1237→**Time allocation:**
  1238→- Track time per work_item (start when created, end when completed)
  1239→- Aggregate to session total
  1240→
  1241→#### Session End Behavior
  1242→
  1243→**When `/msp end` or `/normal` is called:**
  1244→
  1245→1. **Auto-save to database:**
  1246→   - End timer (session.end_time = now)
  1247→   - Calculate session.duration_minutes
  1248→   - Set session.status = 'completed'
  1249→
  1250→2. **Generate dense summary:**
  1251→   ```
  1252→   Session: [Client] - [Project]
  1253→   Duration: [duration]
  1254→   Category: [primary category based on work_items]
  1255→
  1256→   Work Completed:
  1257→   - [Concise bullet: category, title, affected systems]
  1258→   - [Another item]
  1259→
  1260→   Problems Solved: [count]
  1261→   - [Error] → [Solution]
  1262→
  1263→   Infrastructure Changes: [count]
  1264→   - [System]: [change type] - [brief description]
  1265→
  1266→   Commands Run: [count] | Files Modified: [count]
  1267→   Technologies: [tag list]
  1268→
  1269→   Billable: [yes/no] | Hours: [calculated]
  1270→   ```
  1271→
  1272→3. **Prompt for review:**
  1273→   - "Save session? (y/n)"
  1274→   - "Billable hours: [auto-calculated] - adjust? (or press Enter)"
  1275→   - "Add notes? (or press Enter to skip)"
  1276→
  1277→4. **Store to database:**
  1278→   - POST to API: /api/v1/sessions (complete session object)
  1279→   - Include all related: work_items, commands_run, file_changes, etc.
  1280→   - API returns session_id
  1281→
  1282→5. **Generate session log file (optional):**
  1283→   - Create markdown file in session-logs/
  1284→   - Format similar to current session logs but auto-generated
  1285→   - Include all dense information captured
  1286→
  1287→#### Information Density Examples
  1288→
  1289→**Dense (Good):**
  1290→```
  1291→Problem: Apache crash on jupiter
  1292→Error: segfault in mod_php
  1293→Cause: PHP 8.1 incompatibility
  1294→Fix: Downgraded to PHP 7.4, restarted apache
  1295→Verify: Website loads, no errors in logs
  1296→Files: /etc/apache2/mods-enabled/php*.conf
  1297→Commands: 3 (apt, systemctl, curl)
  1298→```
  1299→
  1300→**Verbose (Avoid):**
  1301→```
  1302→I first investigated the Apache crash by checking the error logs.
  1303→Then I noticed that there was a segmentation fault in the mod_php module.
  1304→After some research, I determined this was due to a PHP version incompatibility.
  1305→I proceeded to downgrade PHP from version 8.1 to version 7.4.
  1306→Once that was complete, I restarted the Apache service.
  1307→Finally, I verified the fix by loading the website and checking the logs.
  1308→```
  1309→
  1310→**Dense storage = More information, fewer words.**
  1311→
  1312→#### Credential Handling
  1313→
  1314→**Storage:**
  1315→- New credentials discovered → prompt: "Store credential for [service]? (y/n)"
  1316→- If yes → encrypt and store in credentials table
  1317→- Link to client_id, service_id, infrastructure_id
  1318→
  1319→**Retrieval:**
  1320→- When needed: "Retrieved password for AD2\\sysadmin"
  1321→- Log access to credential_audit_log
  1322→- Display credential in response (since user authorized it)
  1323→
  1324→**Audit:**
  1325→- Every credential access logged with:
  1326→  - Who (JWT user_id)
  1327→  - When (timestamp)
  1328→  - What (credential_id, service_name)
  1329→  - Why (optional context from work_item)
  1330→
  1331→#### Auto-Tagging
  1332→
  1333→**As work progresses, auto-apply tags:**
  1334→- Mention "docker" → tag: docker
  1335→- Working on "jupiter" → tag: jupiter
  1336→- Client "Dataforth" → tag: dataforth
  1337→- Error "connection-timeout" → tag: connection-timeout
  1338→- Action "migration" → tag: migration
  1339→
  1340→**Tag categories:**
  1341→- technology (docker, apache, mariadb)
  1342→- infrastructure (jupiter, pfsense)
  1343→- client (dataforth)
  1344→- problem_type (ssl-error, connection-timeout)
  1345→- action (migration, upgrade, cleanup)
  1346→
  1347→Tags stored in work_item_tags and session_tags junction tables.
  1348→
  1349→#### Context Awareness
  1350→
  1351→**MSP Mode remembers:**
  1352→- Previous sessions for this client (last 5)
  1353→- Open pending_tasks for this client
  1354→- Recent credentials accessed for this client
  1355→- Infrastructure topology for this client
  1356→
  1357→**Auto-suggest:**
  1358→- "Last time we worked on [client], we left off at [pending_task]"
  1359→- "Credentials for [service] are available (last used [date])"
  1360→- "Previous similar issue: [problem from history] - same solution?"
  1361→
  1362→---
  1363→
  1364→### Summary: MSP Mode = Smart Auto-Tracking
  1365→
  1366→1. **Auto-categorize** work as it happens (infrastructure, troubleshooting, etc.)
  1367→2. **Auto-extract** structured data (commands, files, systems, technologies)
  1368→3. **Auto-tag** based on content
  1369→4. **Auto-detect** billability
  1370→5. **Auto-generate** dense summaries (facts, not narrative)
  1371→6. **Auto-link** related data (sessions, problems, credentials)
  1372→7. **Minimal user input** required - Claude does the categorization
  1373→8. **Maximum information density** - brief but complete
  1374→
  1375→User just works, Claude captures everything in structured, queryable format.
  1376→
  1377→---
  1378→
  1379→### MSP Tool Integrations (Future Capability)
  1380→
  1381→**Core Requirement:** MSP Mode will integrate with external MSP platforms to automate workflows and link session data to ticketing/documentation systems.
  1382→
  1383→#### Planned Integrations
  1384→
  1385→**1. SyncroMSP (PSA/RMM Platform)**
  1386→- **Tickets:** Create, update, search, attach files
  1387→- **Time Tracking:** Log billable time from sessions automatically
  1388→- **Assets:** Link session work to customer assets
  1389→- **Contacts:** Associate work with customer contacts
  1390→- **API:** REST API with OAuth
  1391→
  1392→**2. MSP Backups (Backup Management)**
  1393→- **Reports:** Pull backup status reports
  1394→- **Alerts:** Check backup failures
  1395→- **Statistics:** Get backup size, success rates
  1396→- **Export:** Attach reports to tickets
  1397→- **API:** REST API (vendor-specific)
  1398→
  1399→**3. Zapier (Automation Platform)**
  1400→- **Triggers:** Session completed → Create ticket
  1401→- **Actions:** Ticket created → Start session
  1402→- **Webhooks:** Bi-directional automation
  1403→- **API:** REST API + webhooks
  1404→
  1405→**4. Future Integrations**
  1406→- Autotask (PSA)
  1407→- ConnectWise (PSA)
  1408→- Datto RMM
  1409→- IT Glue (Documentation)
  1410→- Microsoft Teams (notifications)
  1411→- Email (session summaries)
  1412→
  1413→#### Example Workflow
  1414→
  1415→**User request:**
  1416→> "Find the ticket for Dataforth about Backups - update it with the changes we've made today, and pull a report from MSP backups to attach to that ticket"
  1417→
  1418→**Claude MSP Mode execution:**
  1419→
  1420→1. **Identify client context:**
  1421→   - Current session: client_id = Dataforth
  1422→   - Search tickets: SyncroMSP.search_tickets(customer="Dataforth", subject contains "Backup")
  1423→
  1424→2. **Find ticket:**
  1425→   - Result: Ticket #12345 "Backup configuration for NAS"
  1426→   - Display to user: "Found ticket #12345: Backup configuration for NAS"
  1427→
  1428→3. **Get today's work:**
  1429→   - Query local database: work_items for current session
  1430→   - Generate summary:
  1431→     ```
  1432→     Changes made today:
  1433→     - Configured Veeam backup job for D2TESTNAS
  1434→     - Set retention: 30 days local, 90 days cloud
  1435→     - Tested backup: successful (45GB)
  1436→     - Verified restore point creation
  1437→     ```
  1438→
  1439→4. **Update ticket:**
  1440→   - SyncroMSP.add_comment(ticket_id=12345, comment=summary)
  1441→   - Log action: infrastructure_changes linked to external_ticket_id
  1442→
  1443→5. **Pull backup report:**
  1444→   - MSPBackups.get_report(customer="Dataforth", date=today)
  1445→   - Download report PDF/CSV
  1446→
  1447→6. **Attach to ticket:**
  1448→   - SyncroMSP.attach_file(ticket_id=12345, file=report)
  1449→   - Confirm to user: "Attached backup report to ticket #12345"
  1450→
  1451→7. **Track integration:**
  1452→   - Store in database:
  1453→     ```sql
  1454→     external_integrations:
  1455→       session_id,
  1456→       integration_type: 'syncro_ticket',
  1457→       external_id: '12345',
  1458→       action: 'updated',
  1459→       details: JSON(comment, attachments)
  1460→     ```
  1461→
  1462→#### Database Schema Additions for Integrations
  1463→
  1464→**New table: `external_integrations`**
  1465→```sql
  1466→CREATE TABLE external_integrations (
  1467→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  1468→    session_id UUID REFERENCES sessions(id) ON DELETE CASCADE,
  1469→    work_item_id UUID REFERENCES work_items(id) ON DELETE CASCADE,
  1470→
  1471→    -- Integration details
  1472→    integration_type VARCHAR(100) NOT NULL, -- 'syncro_ticket', 'msp_backups', 'zapier_webhook'
  1473→    external_id VARCHAR(255), -- ticket ID, asset ID, etc.
  1474→    external_url VARCHAR(500), -- direct link to resource
  1475→
  1476→    -- Action tracking
  1477→    action VARCHAR(50), -- 'created', 'updated', 'linked', 'attached'
  1478→    direction VARCHAR(20), -- 'outbound' (we pushed) or 'inbound' (they triggered)
  1479→
  1480→    -- Data
  1481→    request_data TEXT, -- JSON: what we sent
  1482→    response_data TEXT, -- JSON: what we received
  1483→
  1484→    -- Metadata
  1485→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  1486→    created_by VARCHAR(255), -- user who authorized
  1487→
  1488→    INDEX idx_ext_int_session (session_id),
  1489→    INDEX idx_ext_int_type (integration_type),
  1490→    INDEX idx_ext_int_external (external_id)
  1491→);
  1492→```
  1493→
  1494→**New table: `integration_credentials`**
  1495→```sql
  1496→CREATE TABLE integration_credentials (
  1497→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  1498→    integration_name VARCHAR(100) NOT NULL UNIQUE, -- 'syncro', 'msp_backups', 'zapier'
  1499→
  1500→    -- OAuth or API key
  1501→    credential_type VARCHAR(50) CHECK(credential_type IN ('oauth', 'api_key', 'basic_auth')),
  1502→    api_key_encrypted BYTEA,
  1503→    oauth_token_encrypted BYTEA,
  1504→    oauth_refresh_token_encrypted BYTEA,
  1505→    oauth_expires_at TIMESTAMP,
  1506→
  1507→    -- Endpoints
  1508→    api_base_url VARCHAR(500),
  1509→    webhook_url VARCHAR(500),
  1510→
  1511→    -- Status
  1512→    is_active BOOLEAN DEFAULT true,
  1513→    last_tested_at TIMESTAMP,
  1514→    last_test_status VARCHAR(50),
  1515→
  1516→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  1517→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  1518→
  1519→    INDEX idx_int_cred_name (integration_name)
  1520→);
  1521→```
  1522→
  1523→**New table: `ticket_links`**
  1524→```sql
  1525→CREATE TABLE ticket_links (
  1526→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  1527→    session_id UUID REFERENCES sessions(id) ON DELETE CASCADE,
  1528→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
  1529→
  1530→    -- Ticket info
  1531→    integration_type VARCHAR(100) NOT NULL, -- 'syncro', 'autotask', 'connectwise'
  1532→    ticket_id VARCHAR(255) NOT NULL,
  1533→    ticket_number VARCHAR(100), -- human-readable: "T12345"
  1534→    ticket_subject VARCHAR(500),
  1535→    ticket_url VARCHAR(500),
  1536→    ticket_status VARCHAR(100),
  1537→
  1538→    -- Linking
  1539→    link_type VARCHAR(50), -- 'related', 'resolves', 'documents'
  1540→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  1541→
  1542→    INDEX idx_ticket_session (session_id),
  1543→    INDEX idx_ticket_client (client_id),
  1544→    INDEX idx_ticket_external (integration_type, ticket_id)
  1545→);
  1546→```
  1547→
  1548→#### API Integration Layer
  1549→
  1550→**FastAPI endpoints for integration management:**
  1551→```
  1552→GET    /api/v1/integrations                    (list configured integrations)
  1553→POST   /api/v1/integrations/{name}/test        (test connection)
  1554→GET    /api/v1/integrations/{name}/credentials (get encrypted credentials)
  1555→PUT    /api/v1/integrations/{name}/credentials (update credentials)
  1556→
  1557→# Syncro-specific
  1558→GET    /api/v1/syncro/tickets                  (search tickets)
  1559→POST   /api/v1/syncro/tickets/{id}/comment     (add comment)
  1560→POST   /api/v1/syncro/tickets/{id}/attach      (attach file)
  1561→POST   /api/v1/syncro/time                     (log time entry)
  1562→
  1563→# MSP Backups
  1564→GET    /api/v1/mspbackups/report               (pull report)
  1565→GET    /api/v1/mspbackups/status/{client}      (backup status)
  1566→
  1567→# Zapier webhooks
  1568→POST   /api/v1/webhooks/zapier                 (receive webhook)
  1569→```
  1570→
  1571→#### Workflow Automation
  1572→
  1573→**Session → Ticket Linking:**
  1574→When MSP Mode session ends:
  1575→1. Ask user: "Link this session to a ticket? (y/n/search)"
  1576→2. If search: query Syncro for tickets matching client
  1577→3. If found: link session_id to ticket_id in ticket_links table
  1578→4. Auto-post session summary as ticket comment (optional)
  1579→
  1580→**Auto Time Tracking:**
  1581→When session ends with billable hours:
  1582→1. Ask: "Log 2.5 hours to SyncroMSP? (y/n)"
  1583→2. If yes: POST to Syncro time tracking API
  1584→3. Link time entry ID to session in external_integrations
  1585→
  1586→**Backup Report Automation:**
  1587→Trigger: User mentions "backup" in MSP session for client
  1588→1. Detect keyword "backup"
  1589→2. Auto-suggest: "Pull latest backup report for [Client]? (y/n)"
  1590→3. If yes: Query MSPBackups API, display summary
  1591→4. Option to attach to ticket or save to session
  1592→
  1593→#### Permission & Security
  1594→
  1595→**OAuth Flow:**
  1596→1. User initiates: `/msp integrate syncro`
  1597→2. Claude generates OAuth URL, user authorizes in browser
  1598→3. Callback URL receives token, encrypts, stores in integration_credentials
  1599→4. Refresh token used to maintain access
  1600→
  1601→**API Key Storage:**
  1602→- All integration credentials encrypted with AES-256-GCM
  1603→- Same master key as credential storage
  1604→- Audit log for all integration credential access
  1605→
  1606→**Scopes:**
  1607→- Read-only for initial implementation (search tickets, pull reports)
  1608→- Write access requires explicit user confirmation per action
  1609→- Never auto-update tickets without user approval
  1610→
  1611→#### Future Capabilities
  1612→
  1613→**Natural Language Integration:**
  1614→- "Create a ticket for Dataforth about today's backup work"
  1615→- "Show me all open tickets for Grabb & Durando"
  1616→- "Pull the backup report for last week and email it to [contact]"
  1617→- "Log today's 3 hours to ticket T12345"
  1618→- "What tickets mention Apache or SSL?"
  1619→
  1620→**Multi-Step Workflows:**
  1621→- Session ends → Auto-create ticket → Auto-log time → Auto-attach session summary
  1622→- Backup failure detected (via webhook) → Create session → Investigate → Update ticket
  1623→- Ticket created in Syncro (webhook) → Notify Claude → Start MSP session
  1624→
  1625→**Bi-Directional Sync:**
  1626→- Ticket updated in Syncro → Webhook to Claude → Add to pending_tasks
  1627→- Session completed in Claude → Auto-comment in ticket
  1628→- Time logged in Claude → Synced to Syncro billing
  1629→
  1630→#### Implementation Priority
  1631→
  1632→**Phase 1 (MVP):**
  1633→- Database tables for integrations
  1634→- SyncroMSP ticket search and read
  1635→- Manual ticket linking
  1636→- Session summary → ticket comment (manual)
  1637→
  1638→**Phase 2:**
  1639→- MSP Backups report pulling
  1640→- File attachments to tickets
  1641→- OAuth token refresh automation
  1642→- Auto-suggest ticket linking
  1643→
  1644→**Phase 3:**
  1645→- Zapier webhook triggers
  1646→- Auto time tracking
  1647→- Multi-step workflows
  1648→- Natural language commands
  1649→
  1650→**Phase 4:**
  1651→- Bi-directional sync
  1652→- Advanced automation
  1653→- Additional PSA integrations (Autotask, ConnectWise)
  1654→- IT Glue documentation sync
  1655→
  1656→---
  1657→
  1658→### Impact on Current Architecture
  1659→
  1660→**API Design Considerations:**
  1661→- Modular integration layer (plugins per platform)
  1662→- Webhook receiver endpoints
  1663→- OAuth flow support
  1664→- Rate limiting per integration
  1665→- Retry logic for failed API calls
  1666→
  1667→**Database Design:**
  1668→- external_integrations table (already designed above)
  1669→- integration_credentials table (already designed above)
  1670→- ticket_links table (already designed above)
  1671→- Indexes for fast external_id lookups
  1672→
  1673→**Security:**
  1674→- Integration credentials separate from user credentials
  1675→- Per-integration permission scopes
  1676→- Audit logging for all external API calls
  1677→- User confirmation for write operations
  1678→
  1679→**FastAPI Architecture:**
  1680→```python
  1681→# Integration plugins
  1682→integrations/
  1683→├── __init__.py
  1684→├── base.py              (BaseIntegration abstract class)
  1685→├── syncro.py            (SyncroMSP integration)
  1686→├── mspbackups.py        (MSP Backups integration)
  1687→├── zapier.py            (Zapier webhooks)
  1688→└── future/
  1689→    ├── autotask.py
  1690→    ├── connectwise.py
  1691→    └── itglue.py
  1692→```
  1693→
  1694→This integration capability is foundational to MSP Mode's value proposition - linking real-world MSP workflows to intelligent automation.
  1695→
  1696→### Normal Mode Behaviors (DEFINED)
  1697→
  1698→**Core Principle:** Track valuable work that doesn't belong to a specific client or dev project. General research, internal tasks, exploration, learning.
  1699→
  1700→#### Purpose
  1701→
  1702→Normal Mode is for:
  1703→- **Research and exploration** - "How does JWT authentication work?"
  1704→- **General questions** - "What's the best way to handle SQL migrations?"
  1705→- **Internal infrastructure** (non-client) - Working on Jupiter/Saturn without client context
  1706→- **Learning/experimentation** - Testing new tools, trying approaches
  1707→- **Documentation** - Writing guides, updating READMEs
  1708→- **Non-categorized work** - Anything that doesn't fit MSP or Dev
  1709→
  1710→**Not for:**
  1711→- Client work → Use MSP Mode
  1712→- Specific development projects → Use Dev Mode
  1713→
  1714→#### When `/normal` is Called
  1715→
  1716→1. **Mode Switch:**
  1717→   - If coming from MSP/Dev mode: preserve all knowledge/context from previous mode
  1718→   - Set session context to "general" (no client_id, no project_id)
  1719→   - Display: "Normal Mode | General work session"
  1720→
  1721→2. **Knowledge Retention:**
  1722→   - **Keep:** All learned information, credentials accessed, context from previous modes
  1723→   - **Clear:** Client/project assignment only
  1724→   - **Rationale:** You might research something in Normal mode, then apply it in MSP mode
  1725→
  1726→3. **Session Creation:**
  1727→   - Create session with:
  1728→     - client_id = NULL
  1729→     - project_id = NULL (or link to "Internal" or "Research" pseudo-project)
  1730→     - session_title = "General work session: [auto-generated from topic]"
  1731→     - is_billable = false (by default, since not client work)
  1732→
  1733→#### During Normal Mode Session
  1734→
  1735→**Tracking (lighter than MSP):**
  1736→- Still create work_items, but less granular
  1737→- Track major actions: "Researched FastAPI authentication patterns"
  1738→- Track useful findings: "Found pyjwt library, better than python-jose"
  1739→- Track decisions: "Decided to use Alembic for migrations"
  1740→
  1741→**What gets stored:**
  1742→- Work items with category (usually 'documentation', 'research', or 'development')
  1743→- Key commands run (if applicable)
  1744→- Files modified (if applicable)
  1745→- Tags: technology, topics
  1746→- Brief summary of what was learned/accomplished
  1747→
  1748→**What doesn't get stored:**
  1749→- Less emphasis on billability tracking
  1750→- No client/project relationships
  1751→- Less detailed command/file tracking (unless requested)
  1752→
  1753→#### Information Density in Normal Mode
  1754→
  1755→**Focus on:** Capturing useful knowledge, decisions, findings
  1756→
  1757→**Example Normal Mode work_item:**
  1758→```
  1759→Title: Researched PostgreSQL connection pooling for FastAPI
  1760→Category: research
  1761→Description: Compared SQLAlchemy pooling vs asyncpg.
  1762→Finding: SQLAlchemy pool_size=20, max_overflow=10 optimal for our load.
  1763→Decision: Use SQLAlchemy with pool_pre_ping=True for connection health checks.
  1764→Tags: postgresql, sqlalchemy, fastapi, connection-pooling
  1765→Billable: false
  1766→```
  1767→
  1768→**Not:** "I started by searching for PostgreSQL connection pooling documentation..."
  1769→
  1770→#### Session End (Normal Mode)
  1771→
  1772→**Auto-save:**
  1773→- session.client_id = NULL
  1774→- session.is_billable = false
  1775→- session.summary = brief summary of research/work done
  1776→
  1777→**Generated summary example:**
  1778→```
  1779→Session: General Research
  1780→Duration: 45 minutes
  1781→Category: Research
  1782→
  1783→Topics Explored:
  1784→- FastAPI database connection pooling
  1785→- JWT vs session authentication
  1786→- Alembic migration strategies
  1787→
  1788→Key Findings:
  1789→- SQLAlchemy pooling recommended for our use case
  1790→- JWT refresh tokens better than long-lived access tokens
  1791→- Alembic supports auto-generate from models
  1792→
  1793→Tags: fastapi, postgresql, jwt, alembic
  1794→Billable: No
  1795→```
  1796→
  1797→#### Value of Normal Mode Sessions
  1798→
  1799→**Why track this?**
  1800→1. **Knowledge base** - "What did I learn about X last month?"
  1801→2. **Decision trail** - "Why did we choose technology Y?"
  1802→3. **Reference** - "Where did I see that solution before?"
  1803→4. **Context recovery** - Future Claude instances can search: "Show me research on JWT authentication"
  1804→
  1805→**Queryable:**
  1806→- "What have I researched about Docker networking?"
  1807→- "When did I decide to use FastAPI over Flask?"
  1808→- "Show all sessions tagged 'postgresql'"
  1809→
  1810→#### Mode Comparison
  1811→
  1812→| Aspect | MSP Mode | Dev Mode | Normal Mode |
  1813→|--------|----------|----------|-------------|
  1814→| **Purpose** | Client work | Specific projects | General work/research |
  1815→| **Client/Project** | Required | Optional | None (NULL) |
  1816→| **Billable** | Default: yes | Default: no | Default: no |
  1817→| **Detail Level** | High (every command) | Medium | Light (key actions) |
  1818→| **Focus** | Client value delivery | Code/features | Knowledge/learning |
  1819→| **Session Title** | "[Client] - [Issue]" | "[Project] - [Feature]" | "Research: [Topic]" |
  1820→
  1821→#### Switching Between Modes
  1822→
  1823→**MSP → Normal:**
  1824→- User: "Let me research how to fix this SSL issue"
  1825→- `/normal` → Research mode, but can reference client context if needed
  1826→- Knowledge retained: knows which client, what SSL issue
  1827→- Categorization: research session, not billable client work
  1828→
  1829→**Normal → MSP:**
  1830→- User: "Okay, back to Dataforth"
  1831→- `/msp` → Resumes (or starts new) Dataforth session
  1832→- Knowledge retained: knows solution from research
  1833→- Categorization: client work, billable
  1834→
  1835→**Dev → Normal → MSP:**
  1836→- Modes are fluid, knowledge carries through
  1837→- Only categorization changes
  1838→- Session assignments change (project vs client vs general)
  1839→
  1840→---
  1841→
  1842→### Development Mode Behaviors (To Define)
  1843→1. What should Development Mode track?
  1844→2. How does it differ from MSP and Normal modes?
  1845→3. Integration with git repos?
  1846→
  1847→### Implementation Order
  1848→1. Database schema design
  1849→2. API development and deployment
  1850→3. MCP server or API client for Claude Code
  1851→4. MSP Mode slash command
  1852→5. Development Mode
  1853→6. Normal Mode
  1854→
  1855→---
  1856→
  1857→## Security Considerations
  1858→
  1859→### Credential Storage
  1860→- **Never store plaintext passwords**
  1861→- Use Fernet encryption or AES-256-GCM
  1862→- Encryption key stored separately from database
  1863→- Key rotation strategy needed
  1864→
  1865→### API Security
  1866→- HTTPS only (no HTTP)
  1867→- Rate limiting (prevent brute force)
  1868→- IP whitelisting (optional - VPN only)
  1869→- Token expiration and refresh
  1870→- Revocation list for compromised tokens
  1871→- Audit logging for credential access
  1872→
  1873→### Multi-Machine Sync
  1874→- Encrypted tokens in Gitea config
  1875→- git-crypt or encrypted JSON values
  1876→- Never commit plaintext tokens to repo
  1877→
  1878→---
  1879→
  1880→## Next Steps (Planning Phase)
  1881→
  1882→1. ✅ Architecture decisions (SQL, FastAPI, JWT)
  1883→2. ⏳ Define MSP Mode behaviors in detail
  1884→3. ⏳ Design database schema
  1885→4. ⏳ Define API endpoints specification
  1886→5. ⏳ Create authentication flow diagram
  1887→6. ⏳ Design slash command interactions
  1888→
  1889→---
  1890→
  1891→## Notes
  1892→
  1893→- This spec will evolve as we discuss details
  1894→- Focus on scalability and robustness
  1895→- Design for future team members and integrations
  1896→- All decisions documented with rationale
  1897→
  1898→---
  1899→
  1900→## Change Log
  1901→
  1902→**2026-01-15:**
  1903→- Initial spec created
  1904→- Architecture decisions: SQL, FastAPI, JWT, Gitea config
  1905→- Technology stack defined
  1906→- High-level infrastructure design
  1907→- Open questions identified
  1908→- **Database schema designed:** 27 tables via parallel agent analysis
  1909→  - 5 parallel agents analyzed: sessions, credentials, projects, work categorization, infrastructure
  1910→  - Comprehensive schema with 22 core tables + 5 junction tables
  1911→  - Analyzed 37 session logs, credentials file, all projects, infrastructure docs
  1912→  - Estimated storage: 1-2 GB/year
  1913→  - Pre-identified 157+ tags for categorization
  1914→- **MSP Mode behaviors defined:**
  1915→  - Auto-categorization of client work
  1916→  - Information-dense storage format
  1917→  - Auto-tracking: commands, files, problems, credentials, infrastructure changes
  1918→  - Smart billability detection
  1919→  - Context awareness and auto-suggestion
  1920→- **Normal Mode behaviors defined:**
  1921→  - For general work/research not assigned to client or dev project
  1922→  - Knowledge retention across mode switches
  1923→  - Lighter tracking than MSP mode
  1924→  - Captures decisions, findings, learnings
  1925→  - Queryable knowledge base
  1926→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

# MSP Mode Specification

**Date Started:** 2026-01-15
**Status:** Planning / Design Phase

---

## Overview

Creating a custom "MSP Mode" for Claude Code that tracks client work, maintains context across sessions and machines, and provides structured access to historical MSP data.

## Objectives

1. **Track MSP client work** - Sessions, work items, time, credentials
2. **Multi-machine access** - Same context available on all machines
3. **Long-term context** - Search historical work across clients/projects
4. **Scalable & robust** - Designed to evolve over time

---

## Architecture Decisions

### Storage: SQL Database (MariaDB)

**Decision:** Use SQL database instead of local files + Git sync

**Rationale:**
- Claude Code requires internet anyway (offline not a real advantage)
- Structured queries needed ("show all work for Client X in January")
- Relational data (clients → projects → sessions → credentials → billing)
- Fast indexing and search even with years of data
- No merge conflicts (single source of truth)
- Time tracking and billing calculations
- Report generation capabilities

**Infrastructure:**
- Existing MariaDB on Jupiter (172.16.3.20)
- New database: `msp_tracking`

### Access Method: REST API with JWT Authentication

**Decision:** FastAPI REST API on Jupiter with JWT tokens

**Rationale - Security:**
- Token-based auth (revocable, rotatable)
- Scoped permissions (API can't access other databases)
- Audit logging (track all queries by user/token/timestamp)
- Rate limiting possible
- HTTPS encryption in transit
- Token revocation without DB password changes
- IP restrictions and 2FA possible later

**Rationale - Scalability:**
- Industry standard approach
- Can add team members later
- Other tools can integrate (scripts, mobile, GuruRMM)
- Stateless authentication (no session storage)
- Multiple clients supported

**Rationale - Robustness:**
- Comprehensive error handling
- Input validation before DB access
- Structured logging
- Health checks and monitoring
- Version controlled schema migrations

### Technology Stack

**API Framework: FastAPI (Python)**
- Async performance for concurrent requests
- Auto-generated OpenAPI/Swagger documentation
- Type safety with Pydantic models (runtime validation)
- SQLAlchemy ORM for complex queries
- Built-in background tasks
- Industry-standard testing with pytest
- Alembic for database migrations
- Mature dependency injection

**Authentication: JWT Tokens**
- Stateless (no DB lookup to validate)
- Claims-based (permissions, scopes, expiration)
- Refresh token pattern for long-term access
- Multiple clients/machines supported
- Short-lived tokens minimize compromise risk
- Industry standard

**Configuration Storage: Gitea (Private Repo)**
- Multi-machine sync
- Version controlled
- Single source of truth
- Token rotation = one commit, all machines sync
- Encrypted token values (git-crypt or encrypted JSON)
- Backup via Gitea

**Deployment: Docker Container**
- Easy deployment and updates
- Resource limits
- Systemd service for auto-restart
- Portable (can migrate to dedicated host later)

---

## Infrastructure Design

### Jupiter Server (172.16.3.20)

**Docker Container: msp-api**
- FastAPI application (Python 3.11+)
- SQLAlchemy + Alembic (ORM and migrations)
- JWT auth library (python-jose)
- Pydantic validation
- Gunicorn/Uvicorn ASGI server
- Health checks endpoint
- Prometheus metrics (optional)
- Mounted logs: /var/log/msp-api/

**MariaDB Database: msp_tracking**
- Connection pooling (SQLAlchemy)
- Automated backups (critical MSP data)
- Schema versioned with Alembic

**Nginx Reverse Proxy**
- HTTPS with Let's Encrypt
- Rate limiting
- Access logs
- Proxies to: msp-api.azcomputerguru.com

### Gitea Private Repository

**Repo: azcomputerguru/claude-settings (or new msp-config repo)**

Structure:
```
msp-api-config.json
├── api_url (https://msp-api.azcomputerguru.com)
├── api_token (encrypted JWT or refresh token)
└── database_schema_version (for migration tracking)
```

### Local Machine (D:\ClaudeTools)

**Directory Structure:**
```
D:\ClaudeTools\
├── .claude/
│   ├── commands/
│   │   ├── msp.md (MSP Mode slash command)
│   │   ├── dev.md (Development Mode - TBD)
│   │   └── normal.md (Normal Mode - TBD)
│   └── msp-api-config.json (synced from Gitea)
├── MSP-MODE-SPEC.md (this file)
└── .git/ (synced to Gitea)
```

---

## API Design Principles

### Versioning
- Start with `/api/v1/` from day one
- Allows breaking changes in future versions

### Security
- All endpoints require JWT authentication
- Input validation with Pydantic models
- Never expose database errors to client
- Rate limiting to prevent abuse
- Comprehensive audit logging

### Endpoints (Draft - To Be Detailed)
```
POST   /api/v1/sessions          (start new MSP session)
GET    /api/v1/sessions          (query sessions - filters: client, date range, etc.)
POST   /api/v1/work-items        (log work performed)
GET    /api/v1/clients           (list clients)
POST   /api/v1/clients           (create client record)
GET    /api/v1/clients/{id}/credentials
POST   /api/v1/auth/token        (get JWT token)
POST   /api/v1/auth/refresh      (refresh expired token)
GET    /api/v1/health            (health check)
GET    /api/v1/metrics           (Prometheus metrics - optional)
```

### Error Handling
- Structured JSON error responses
- HTTP status codes (400, 401, 403, 404, 429, 500)
- Never leak sensitive information in errors
- Log all errors with context

### Logging
- JSON structured logs (easy parsing)
- Log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
- Include: timestamp, user/token ID, endpoint, duration, status
- Separate audit log for sensitive operations (credential access, deletions)

---

## JWT Token Structure

### Access Token (Short-lived: 1 hour)
```json
{
  "sub": "mike@azcomputerguru.com",
  "scopes": ["msp:read", "msp:write", "msp:admin"],
  "machine": "windows-workstation",
  "exp": 1234567890,
  "iat": 1234567890,
  "jti": "unique-token-id"
}
```

### Refresh Token (Long-lived: 30 days)
- Stored securely in Gitea config
- Used to obtain new access tokens
- Can be revoked server-side

### Scopes (Permissions)
- `msp:read` - Read sessions, clients, work items
- `msp:write` - Create/update sessions, work items
- `msp:admin` - Manage clients, credentials, delete operations

---

## Database Schema (Draft - To Be Detailed)

### Tables (High-Level)
```
clients
- id, name, network_cidr, primary_contact, notes, created_at, updated_at

projects
- id, client_id, name, description, status, created_at, updated_at

sessions
- id, project_id, start_time, end_time, billable_hours, notes, created_at

work_items
- id, session_id, description, category, timestamp, billable, created_at

credentials
- id, client_id, service, username, password_encrypted, notes, created_at, updated_at

tags (for categorization)
- id, name, type (client_tag, project_tag, work_tag)

session_tags (many-to-many)
- session_id, tag_id
```

### Schema Versioning
- Alembic migrations in version control
- Schema version tracked in config
- Automated migration on API startup (optional)

---

## Modes Overview (D:\ClaudeTools Context)

### 1. MSP Mode
- **Purpose:** Track client work, maintain context across sessions
- **Activation:** `/msp` slash command
- **Behaviors:** (To Be Detailed)
  - Prompt for client/project at start
  - Auto-log work items as we work
  - Track time spent
  - Save credentials securely
  - Generate session summary at end

### 2. Development Mode
- **Purpose:** (To Be Detailed)
- **Activation:** `/dev` slash command
- **Behaviors:** (To Be Detailed)

### 3. Normal Mode
- **Purpose:** Return to standard Claude behavior
- **Activation:** `/normal` slash command
- **Behaviors:** (To Be Detailed)
  - Clear active mode context
  - Standard conversational Claude

---

## Database Schema Design

**Status:** ✅ Analyzed via 5 parallel agents on 2026-01-15

Based on comprehensive analysis of:
- 37 session logs (Dec 2025 - Jan 2026)
- shared-data/credentials.md
- All project directories and documentation
- Infrastructure and client network patterns

### Schema Summary

**Total Tables:** 25 core tables + 5 junction tables = **30 tables**

**Categories:**
1. Core MSP Tracking (5 tables)
2. Client & Infrastructure (7 tables)
3. Credentials & Security (4 tables)
4. Work Details (6 tables)
5. Tagging & Categorization (3 tables)
6. System & Audit (2 tables)
7. **External Integrations (3 tables)** - Added 2026-01-15

---

### 1. Core MSP Tracking Tables (5 tables)

#### `clients`
Master table for all client organizations.

```sql
CREATE TABLE clients (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(255) NOT NULL UNIQUE,
    type VARCHAR(50) NOT NULL CHECK(type IN ('msp_client', 'internal', 'project')),
    network_subnet VARCHAR(100), -- e.g., "192.168.0.0/24"
    domain_name VARCHAR(255), -- AD domain or primary domain
    m365_tenant_id UUID, -- Microsoft 365 tenant ID
    primary_contact VARCHAR(255),
    notes TEXT,
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_clients_type (type),
    INDEX idx_clients_name (name)
);
```

**Examples:** Dataforth, Grabb & Durando, Valley Wide Plastering, AZ Computer Guru (internal)

---

#### `projects`
Individual projects/engagements for clients.

```sql
CREATE TABLE projects (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL,
    slug VARCHAR(255) UNIQUE, -- directory name: "dataforth-dos"
    category VARCHAR(50) CHECK(category IN (
        'client_project', 'internal_product', 'infrastructure',
        'website', 'development_tool', 'documentation'
    )),
    status VARCHAR(50) DEFAULT 'working' CHECK(status IN (
        'complete', 'working', 'blocked', 'pending', 'critical', 'deferred'
    )),
    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
    description TEXT,
    started_date DATE,
    target_completion_date DATE,
    completed_date DATE,
    estimated_hours DECIMAL(10,2),
    actual_hours DECIMAL(10,2),
    gitea_repo_url VARCHAR(500),
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_projects_client (client_id),
    INDEX idx_projects_status (status),
    INDEX idx_projects_slug (slug)
);
```

**Examples:** dataforth-dos, gururmm, grabb-website-move

---

#### `sessions`
Work sessions with time tracking.

```sql
CREATE TABLE sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID REFERENCES clients(id) ON DELETE SET NULL,
    project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
    session_date DATE NOT NULL,
    start_time TIMESTAMP,
    end_time TIMESTAMP,
    duration_minutes INTEGER, -- auto-calculated or manual
    status VARCHAR(50) DEFAULT 'completed' CHECK(status IN (
        'completed', 'in_progress', 'blocked', 'pending'
    )),
    session_title VARCHAR(500) NOT NULL,
    summary TEXT, -- markdown summary
    is_billable BOOLEAN DEFAULT false,
    billable_hours DECIMAL(10,2),
    technician VARCHAR(255), -- "Mike Swanson", etc.
    session_log_file VARCHAR(500), -- path to .md file
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_sessions_client (client_id),
    INDEX idx_sessions_project (project_id),
    INDEX idx_sessions_date (session_date),
    INDEX idx_sessions_billable (is_billable)
);
```

---

#### `work_items`
Individual tasks/actions within sessions (granular tracking).

```sql
CREATE TABLE work_items (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    category VARCHAR(50) NOT NULL CHECK(category IN (
        'infrastructure', 'troubleshooting', 'configuration',
        'development', 'maintenance', 'security', 'documentation'
    )),
    title VARCHAR(500) NOT NULL,
    description TEXT NOT NULL,
    status VARCHAR(50) DEFAULT 'completed' CHECK(status IN (
        'completed', 'in_progress', 'blocked', 'pending', 'deferred'
    )),
    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
    is_billable BOOLEAN DEFAULT false,
    estimated_minutes INTEGER,
    actual_minutes INTEGER,
    affected_systems TEXT, -- JSON array: ["jupiter", "172.16.3.20"]
    technologies_used TEXT, -- JSON array: ["docker", "mariadb"]
    item_order INTEGER, -- sequence within session
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    completed_at TIMESTAMP,

    INDEX idx_work_items_session (session_id),
    INDEX idx_work_items_category (category),
    INDEX idx_work_items_status (status)
);
```

**Categories distribution (from analysis):**
- Infrastructure: 30%
- Troubleshooting: 25%
- Configuration: 15%
- Development: 15%
- Maintenance: 10%
- Security: 5%

---

#### `pending_tasks`
Open items across all clients/projects.

```sql
CREATE TABLE pending_tasks (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
    project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
    work_item_id UUID REFERENCES work_items(id) ON DELETE SET NULL,
    title VARCHAR(500) NOT NULL,
    description TEXT,
    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
    blocked_by TEXT, -- what's blocking this
    assigned_to VARCHAR(255),
    due_date DATE,
    status VARCHAR(50) DEFAULT 'pending' CHECK(status IN (
        'pending', 'in_progress', 'blocked', 'completed', 'cancelled'
    )),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    completed_at TIMESTAMP,

    INDEX idx_pending_tasks_client (client_id),
    INDEX idx_pending_tasks_status (status),
    INDEX idx_pending_tasks_priority (priority)
);
```

---

### 2. Client & Infrastructure Tables (7 tables)

#### `sites`
Physical/logical locations for clients.

```sql
CREATE TABLE sites (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
    name VARCHAR(255) NOT NULL, -- "Main Office", "SLC - Salt Lake City"
    network_subnet VARCHAR(100), -- "172.16.9.0/24"
    vpn_required BOOLEAN DEFAULT false,
    vpn_subnet VARCHAR(100), -- "192.168.1.0/24"
    gateway_ip VARCHAR(45), -- IPv4/IPv6
    dns_servers TEXT, -- JSON array
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_sites_client (client_id)
);
```

---

#### `infrastructure`
Servers, network devices, NAS, workstations.

```sql
CREATE TABLE infrastructure (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
    site_id UUID REFERENCES sites(id) ON DELETE SET NULL,
    asset_type VARCHAR(50) NOT NULL CHECK(asset_type IN (
        'physical_server', 'virtual_machine', 'container',
        'network_device', 'nas_storage', 'workstation',
        'firewall', 'domain_controller'
    )),
    hostname VARCHAR(255) NOT NULL,
    ip_address VARCHAR(45),
    mac_address VARCHAR(17),
    os VARCHAR(255), -- "Ubuntu 22.04", "Windows Server 2022", "Unraid"
    role_description TEXT, -- "Primary DC, NPS/RADIUS server"
    parent_host_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL, -- for VMs/containers
    status VARCHAR(50) DEFAULT 'active' CHECK(status IN (
        'active', 'migration_source', 'migration_destination', 'decommissioned'
    )),
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_infrastructure_client (client_id),
    INDEX idx_infrastructure_type (asset_type),
    INDEX idx_infrastructure_hostname (hostname),
    INDEX idx_infrastructure_parent (parent_host_id)
);
```

**Examples:** Jupiter, AD2 (Dataforth), VWP-DC1, D2TESTNAS

---

#### `services`
Applications/services running on infrastructure.

```sql
CREATE TABLE services (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
    service_name VARCHAR(255) NOT NULL, -- "Gitea", "PostgreSQL", "Apache"
    service_type VARCHAR(100), -- "git_hosting", "database", "web_server"
    external_url VARCHAR(500), -- "https://git.azcomputerguru.com"
    internal_url VARCHAR(500), -- "http://172.16.3.20:3000"
    port INTEGER,
    protocol VARCHAR(50), -- "https", "ssh", "smb"
    status VARCHAR(50) DEFAULT 'running' CHECK(status IN (
        'running', 'stopped', 'error', 'maintenance'
    )),
    version VARCHAR(100),
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_services_infrastructure (infrastructure_id),
    INDEX idx_services_name (service_name),
    INDEX idx_services_type (service_type)
);
```

---

#### `service_relationships`
Dependencies and relationships between services.

```sql
CREATE TABLE service_relationships (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    from_service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
    to_service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
    relationship_type VARCHAR(50) NOT NULL CHECK(relationship_type IN (
        'hosted_on', 'proxied_by', 'authenticates_via',
        'backend_for', 'depends_on', 'replicates_to'
    )),
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    UNIQUE(from_service_id, to_service_id, relationship_type),
    INDEX idx_service_rel_from (from_service_id),
    INDEX idx_service_rel_to (to_service_id)
);
```

**Examples:**
- Gitea (proxied_by) NPM
- GuruRMM API (hosted_on) Jupiter container

---

#### `networks`
Network segments, VLANs, VPN networks.

```sql
CREATE TABLE networks (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
    site_id UUID REFERENCES sites(id) ON DELETE CASCADE,
    network_name VARCHAR(255) NOT NULL,
    network_type VARCHAR(50) CHECK(network_type IN (
        'lan', 'vpn', 'vlan', 'isolated', 'dmz'
    )),
    cidr VARCHAR(100) NOT NULL, -- "192.168.0.0/24"
    gateway_ip VARCHAR(45),
    vlan_id INTEGER,
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_networks_client (client_id),
    INDEX idx_networks_site (site_id)
);
```

---

#### `firewall_rules`
Network security rules (for documentation/audit trail).

```sql
CREATE TABLE firewall_rules (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
    rule_name VARCHAR(255),
    source_cidr VARCHAR(100),
    destination_cidr VARCHAR(100),
    port INTEGER,
    protocol VARCHAR(20), -- "tcp", "udp", "icmp"
    action VARCHAR(20) CHECK(action IN ('allow', 'deny', 'drop')),
    rule_order INTEGER,
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255),

    INDEX idx_firewall_infra (infrastructure_id)
);
```

---

#### `m365_tenants`
Microsoft 365 tenant tracking.

```sql
CREATE TABLE m365_tenants (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
    tenant_id UUID NOT NULL UNIQUE, -- Microsoft tenant ID
    tenant_name VARCHAR(255), -- "dataforth.com"
    default_domain VARCHAR(255), -- "dataforthcorp.onmicrosoft.com"
    admin_email VARCHAR(255),
    cipp_name VARCHAR(255), -- name in CIPP portal
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_m365_client (client_id),
    INDEX idx_m365_tenant_id (tenant_id)
);
```

---

### 3. Credentials & Security Tables (4 tables)

#### `credentials`
Encrypted credential storage (values encrypted at rest).

```sql
CREATE TABLE credentials (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
    service_id UUID REFERENCES services(id) ON DELETE CASCADE,
    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
    credential_type VARCHAR(50) NOT NULL CHECK(credential_type IN (
        'password', 'api_key', 'oauth', 'ssh_key',
        'shared_secret', 'jwt', 'connection_string', 'certificate'
    )),
    service_name VARCHAR(255) NOT NULL, -- "Gitea Admin", "AD2 sysadmin"
    username VARCHAR(255),
    password_encrypted BYTEA, -- AES-256-GCM encrypted
    api_key_encrypted BYTEA,
    client_id_oauth VARCHAR(255), -- for OAuth
    client_secret_encrypted BYTEA,
    tenant_id_oauth VARCHAR(255),
    public_key TEXT, -- for SSH
    token_encrypted BYTEA,
    connection_string_encrypted BYTEA,
    integration_code VARCHAR(255), -- for services like Autotask

    -- Metadata
    external_url VARCHAR(500),
    internal_url VARCHAR(500),
    custom_port INTEGER,
    role_description VARCHAR(500),
    requires_vpn BOOLEAN DEFAULT false,
    requires_2fa BOOLEAN DEFAULT false,
    ssh_key_auth_enabled BOOLEAN DEFAULT false,
    access_level VARCHAR(100),

    -- Lifecycle
    expires_at TIMESTAMP,
    last_rotated_at TIMESTAMP,
    is_active BOOLEAN DEFAULT true,

    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_credentials_client (client_id),
    INDEX idx_credentials_service (service_id),
    INDEX idx_credentials_type (credential_type),
    INDEX idx_credentials_active (is_active)
);
```

**Security:**
- All sensitive fields encrypted with AES-256-GCM
- Encryption key stored separately (environment variable or vault)
- Master password unlock mechanism

---

#### `credential_audit_log`
Audit trail for credential access.

```sql
CREATE TABLE credential_audit_log (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    credential_id UUID NOT NULL REFERENCES credentials(id) ON DELETE CASCADE,
    action VARCHAR(50) NOT NULL CHECK(action IN (
        'view', 'create', 'update', 'delete', 'rotate', 'decrypt'
    )),
    user_id VARCHAR(255) NOT NULL, -- JWT sub claim
    ip_address VARCHAR(45),
    user_agent TEXT,
    details TEXT, -- JSON: what changed, why
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_cred_audit_credential (credential_id),
    INDEX idx_cred_audit_user (user_id),
    INDEX idx_cred_audit_timestamp (timestamp)
);
```

---

#### `security_incidents`
Track security events and remediation.

```sql
CREATE TABLE security_incidents (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
    service_id UUID REFERENCES services(id) ON DELETE SET NULL,
    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
    incident_type VARCHAR(100) CHECK(incident_type IN (
        'bec', 'backdoor', 'malware', 'unauthorized_access',
        'data_breach', 'phishing', 'ransomware', 'brute_force'
    )),
    incident_date TIMESTAMP NOT NULL,
    severity VARCHAR(50) CHECK(severity IN ('critical', 'high', 'medium', 'low')),
    description TEXT NOT NULL,
    findings TEXT, -- investigation results
    remediation_steps TEXT,
    status VARCHAR(50) DEFAULT 'investigating' CHECK(status IN (
        'investigating', 'contained', 'resolved', 'monitoring'
    )),
    resolved_at TIMESTAMP,
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_incidents_client (client_id),
    INDEX idx_incidents_type (incident_type),
    INDEX idx_incidents_status (status)
);
```

**Examples:** BG Builders OAuth backdoor, CW Concrete BEC

---

#### `credential_permissions`
Access control for credentials (future team expansion).

```sql
CREATE TABLE credential_permissions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    credential_id UUID NOT NULL REFERENCES credentials(id) ON DELETE CASCADE,
    user_id VARCHAR(255) NOT NULL, -- or role_id
    permission_level VARCHAR(50) CHECK(permission_level IN ('read', 'write', 'admin')),
    granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    granted_by VARCHAR(255),

    UNIQUE(credential_id, user_id),
    INDEX idx_cred_perm_credential (credential_id),
    INDEX idx_cred_perm_user (user_id)
);
```

---

### 4. Work Details Tables (6 tables)

#### `file_changes`
Track files created/modified/deleted during sessions.

```sql
CREATE TABLE file_changes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    file_path VARCHAR(1000) NOT NULL,
    change_type VARCHAR(50) CHECK(change_type IN (
        'created', 'modified', 'deleted', 'renamed', 'backed_up'
    )),
    backup_path VARCHAR(1000),
    size_bytes BIGINT,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_file_changes_work_item (work_item_id),
    INDEX idx_file_changes_session (session_id)
);
```

---

#### `commands_run`
Shell/PowerShell/SQL commands executed.

```sql
CREATE TABLE commands_run (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    command_text TEXT NOT NULL,
    host VARCHAR(255), -- where executed: "jupiter", "172.16.3.20"
    shell_type VARCHAR(50), -- "bash", "powershell", "sql", "docker"
    success BOOLEAN,
    output_summary TEXT, -- first/last lines or error
    execution_order INTEGER, -- sequence within work item
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_commands_work_item (work_item_id),
    INDEX idx_commands_session (session_id),
    INDEX idx_commands_host (host)
);
```

---

#### `infrastructure_changes`
Audit trail for infrastructure modifications.

```sql
CREATE TABLE infrastructure_changes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
    change_type VARCHAR(50) CHECK(change_type IN (
        'dns', 'firewall', 'routing', 'ssl', 'container',
        'service_config', 'hardware', 'network', 'storage'
    )),
    target_system VARCHAR(255) NOT NULL,
    before_state TEXT,
    after_state TEXT,
    is_permanent BOOLEAN DEFAULT true,
    rollback_procedure TEXT,
    verification_performed BOOLEAN DEFAULT false,
    verification_notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_infra_changes_work_item (work_item_id),
    INDEX idx_infra_changes_session (session_id),
    INDEX idx_infra_changes_infrastructure (infrastructure_id)
);
```

---

#### `problem_solutions`
Issue tracking with root cause and resolution.

```sql
CREATE TABLE problem_solutions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    problem_description TEXT NOT NULL,
    symptom TEXT, -- what user saw
    error_message TEXT, -- exact error code/message
    investigation_steps TEXT, -- JSON array of diagnostic commands
    root_cause TEXT,
    solution_applied TEXT NOT NULL,
    verification_method TEXT,
    rollback_plan TEXT,
    recurrence_count INTEGER DEFAULT 1, -- if same problem reoccurs
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_problems_work_item (work_item_id),
    INDEX idx_problems_session (session_id)
);
```

---

#### `deployments`
Track software/config deployments.

```sql
CREATE TABLE deployments (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
    service_id UUID REFERENCES services(id) ON DELETE SET NULL,
    deployment_type VARCHAR(50) CHECK(deployment_type IN (
        'code', 'config', 'database', 'container', 'service_restart'
    )),
    version VARCHAR(100),
    description TEXT,
    deployed_from VARCHAR(500), -- source path or repo
    deployed_to VARCHAR(500), -- destination
    rollback_available BOOLEAN DEFAULT false,
    rollback_procedure TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_deployments_work_item (work_item_id),
    INDEX idx_deployments_infrastructure (infrastructure_id),
    INDEX idx_deployments_service (service_id)
);
```

---

#### `database_changes`
Track database schema/data modifications.

```sql
CREATE TABLE database_changes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    database_name VARCHAR(255) NOT NULL,
    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
    change_type VARCHAR(50) CHECK(change_type IN (
        'schema', 'data', 'index', 'optimization', 'cleanup', 'migration'
    )),
    sql_executed TEXT,
    rows_affected BIGINT,
    size_freed_bytes BIGINT, -- for cleanup operations
    backup_taken BOOLEAN DEFAULT false,
    backup_location VARCHAR(500),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_db_changes_work_item (work_item_id),
    INDEX idx_db_changes_database (database_name)
);
```

---

### 5. Tagging & Categorization Tables (3 tables)

#### `tags`
Flexible tagging system for work items.

```sql
CREATE TABLE tags (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100) UNIQUE NOT NULL,
    category VARCHAR(50) CHECK(category IN (
        'technology', 'client', 'infrastructure',
        'problem_type', 'action', 'service'
    )),
    description TEXT,
    usage_count INTEGER DEFAULT 0, -- auto-increment on use
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_tags_category (category),
    INDEX idx_tags_name (name)
);
```

**Pre-populated tags:** 157+ tags identified from analysis
- 58 technology tags (docker, postgresql, apache, etc.)
- 24 infrastructure tags (jupiter, saturn, pfsense, etc.)
- 20+ client tags
- 30 problem type tags (connection-timeout, ssl-error, etc.)
- 25 action tags (migration, upgrade, cleanup, etc.)

---

#### `work_item_tags` (Junction Table)
Many-to-many relationship: work items ↔ tags.

```sql
CREATE TABLE work_item_tags (
    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
    tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
    PRIMARY KEY (work_item_id, tag_id),

    INDEX idx_wit_work_item (work_item_id),
    INDEX idx_wit_tag (tag_id)
);
```

---

#### `session_tags` (Junction Table)
Many-to-many relationship: sessions ↔ tags.

```sql
CREATE TABLE session_tags (
    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
    tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
    PRIMARY KEY (session_id, tag_id),

    INDEX idx_st_session (session_id),
    INDEX idx_st_tag (tag_id)
);
```

---

### 6. System & Audit Tables (2 tables)

#### `api_audit_log`
Track all API requests for security and debugging.

```sql
CREATE TABLE api_audit_log (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id VARCHAR(255) NOT NULL, -- JWT sub claim
    endpoint VARCHAR(500) NOT NULL, -- "/api/v1/sessions"
    http_method VARCHAR(10), -- GET, POST, PUT, DELETE
    ip_address VARCHAR(45),
    user_agent TEXT,
    request_body TEXT, -- sanitized (no credentials)
    response_status INTEGER, -- 200, 401, 500
    response_time_ms INTEGER,
    error_message TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_api_audit_user (user_id),
    INDEX idx_api_audit_endpoint (endpoint),
    INDEX idx_api_audit_timestamp (timestamp),
    INDEX idx_api_audit_status (response_status)
);
```

---

#### `schema_migrations`
Track database schema versions (Alembic migrations).

```sql
CREATE TABLE schema_migrations (
    version_id VARCHAR(100) PRIMARY KEY,
    description TEXT,
    applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    applied_by VARCHAR(255),
    migration_sql TEXT
);
```

---

### Junction Tables Summary

1. **work_item_tags** - Work items ↔ Tags
2. **session_tags** - Sessions ↔ Tags
3. **project_relationships** (optional) - Projects ↔ Projects (related/dependent)
4. **project_session_logs** (optional) - Projects ↔ Sessions (many-to-many if sessions span multiple projects)

---

### Schema Statistics

**Total Tables:** 30
- Core MSP: 5 tables
- Client & Infrastructure: 7 tables
- Credentials & Security: 4 tables
- Work Details: 6 tables
- Tagging: 3 tables (+ 2 junction)
- System: 2 tables
- External Integrations: 3 tables

**Estimated Row Counts (1 year of MSP work):**
- sessions: ~500-1000 (2-3 per day)
- work_items: ~5,000-10,000 (5-10 per session)
- file_changes: ~10,000
- commands_run: ~20,000
- tags: ~200
- clients: ~50
- projects: ~100
- credentials: ~500
- api_audit_log: ~100,000+

**Storage Estimate:** ~1-2 GB per year (compressed)

---

### Design Principles Applied

1. **Normalized Structure** - Minimizes data duplication
2. **Flexible Tagging** - Supports evolving categorization
3. **Audit Trail** - Comprehensive logging for security and troubleshooting
4. **Scalability** - Designed for multi-user MSP team growth
5. **Security First** - Encrypted credentials, access control, audit logging
6. **Temporal Tracking** - created_at, updated_at, completed_at timestamps
7. **Soft Deletes** - is_active flags allow recovery
8. **Relationships** - Foreign keys enforce referential integrity
9. **Indexes** - Strategic indexes for common query patterns
10. **JSON Flexibility** - JSON fields for arrays/flexible data (affected_systems, technologies_used)

---

## Next Steps for Database Implementation

1. ✅ Schema designed (27 tables, relationships defined)
2. ⏳ Create Alembic migration files
3. ⏳ Set up encryption key management
4. ⏳ Seed initial data (tags, MSP infrastructure)
5. ⏳ Create database on Jupiter MariaDB
6. ⏳ Build FastAPI models (SQLAlchemy + Pydantic)
7. ⏳ Implement API endpoints
8. ⏳ Create authentication flow
9. ⏳ Build MSP Mode slash command integration

---

## Open Questions

### MSP Mode Behaviors (DEFINED)

**Core Principle:** Automatically categorize client interactions and store useful data in brief but information-dense format.

#### When `/msp` is Called (Session Start)

1. **Client/Project Detection:**
   - Auto-detect from context if possible (mentions of client names, domains, IPs)
   - If ambiguous, present quick-select list of recent clients
   - Prompt: "Working on: [Client] - [Project]? (or select different)"

2. **Session Initialization:**
   - Start timer automatically
   - Create session record with:
     - session_date (today)
     - start_time (now)
     - client_id, project_id (detected or selected)
     - status = 'in_progress'

3. **Context Display:**
   - Show brief summary: "MSP Mode: [Client] - [Project] | Started: [time]"
   - Load relevant context: recent sessions, open tasks, credentials for this client

#### During Session (Automatic Tracking)

**Auto-Categorization - Work Items:**
As work progresses, Claude automatically creates work_items with:

- **Category detection:**
  - Keywords trigger categories:
    - "ssh", "docker restart" → infrastructure
    - "error", "not working", "broken" → troubleshooting
    - "configure", "setup", "change settings" → configuration
    - "build", "code", "implement" → development
    - "cleanup", "optimize", "backup" → maintenance
    - "malware", "breach", "unauthorized" → security

- **Technology tagging:**
  - Auto-detect from commands/context: docker, apache, mariadb, m365, etc.
  - Add to work_item.technologies_used array

- **Affected systems:**
  - Extract IPs, hostnames from commands
  - Add to work_item.affected_systems array

**Information-Dense Data Capture:**

1. **Commands Run:**
   - Auto-log every bash/powershell/SQL command executed
   - Store: command_text, host, shell_type, success, output_summary (first/last 5 lines)
   - Link to current work_item

2. **File Changes:**
   - Track when files are read/edited/written
   - Store: file_path, change_type, backup_path (if created), size_bytes
   - Brief description (auto-generated: "Modified Apache config for SSL")

3. **Problems & Solutions:**
   - When user describes an error, auto-create problem_solution record:
     - symptom: "Users can't access website"
     - error_message: "ERR_CONNECTION_TIMED_OUT"
     - investigation_steps: [array of diagnostic commands]
     - root_cause: "Firewall blocking port 443"
     - solution_applied: "Added iptables ACCEPT rule for 443"
     - verification_method: "curl test successful"

4. **Credentials Accessed:**
   - When retrieving credentials, log to credential_audit_log:
     - action: 'decrypt' or 'view'
     - credential_id
     - user_id (from JWT)
     - timestamp
   - Don't log the actual credential value (security)

5. **Infrastructure Changes:**
   - Detect infrastructure modifications:
     - DNS changes → change_type: 'dns'
     - Firewall rules → change_type: 'firewall'
     - Service configs → change_type: 'service_config'
   - Store before_state, after_state, rollback_procedure

**Concise Summaries:**
- Auto-generate brief descriptions:
  - Work item title: "Fixed Apache SSL certificate expiration on jupiter"
  - Problem description: "Website down: cert expired, renewed via certbot, verified"
  - Not verbose: avoid "I then proceeded to...", just facts

#### Billability Detection

**Auto-flag billable work:**
- Client work (non-internal) → is_billable = true by default
- Internal infrastructure → is_billable = false
- User can override with quick command: "/billable false"

**Time allocation:**
- Track time per work_item (start when created, end when completed)
- Aggregate to session total

#### Session End Behavior

**When `/msp end` or `/normal` is called:**

1. **Auto-save to database:**
   - End timer (session.end_time = now)
   - Calculate session.duration_minutes
   - Set session.status = 'completed'

2. **Generate dense summary:**
   ```
   Session: [Client] - [Project]
   Duration: [duration]
   Category: [primary category based on work_items]

   Work Completed:
   - [Concise bullet: category, title, affected systems]
   - [Another item]

   Problems Solved: [count]
   - [Error] → [Solution]

   Infrastructure Changes: [count]
   - [System]: [change type] - [brief description]

   Commands Run: [count] | Files Modified: [count]
   Technologies: [tag list]

   Billable: [yes/no] | Hours: [calculated]
   ```

3. **Prompt for review:**
   - "Save session? (y/n)"
   - "Billable hours: [auto-calculated] - adjust? (or press Enter)"
   - "Add notes? (or press Enter to skip)"

4. **Store to database:**
   - POST to API: /api/v1/sessions (complete session object)
   - Include all related: work_items, commands_run, file_changes, etc.
   - API returns session_id

5. **Generate session log file (optional):**
   - Create markdown file in session-logs/
   - Format similar to current session logs but auto-generated
   - Include all dense information captured

#### Information Density Examples

**Dense (Good):**
```
Problem: Apache crash on jupiter
Error: segfault in mod_php
Cause: PHP 8.1 incompatibility
Fix: Downgraded to PHP 7.4, restarted apache
Verify: Website loads, no errors in logs
Files: /etc/apache2/mods-enabled/php*.conf
Commands: 3 (apt, systemctl, curl)
```

**Verbose (Avoid):**
```
I first investigated the Apache crash by checking the error logs.
Then I noticed that there was a segmentation fault in the mod_php module.
After some research, I determined this was due to a PHP version incompatibility.
I proceeded to downgrade PHP from version 8.1 to version 7.4.
Once that was complete, I restarted the Apache service.
Finally, I verified the fix by loading the website and checking the logs.
```

**Dense storage = More information, fewer words.**

#### Credential Handling

**Storage:**
- New credentials discovered → prompt: "Store credential for [service]? (y/n)"
- If yes → encrypt and store in credentials table
- Link to client_id, service_id, infrastructure_id

**Retrieval:**
- When needed: "Retrieved password for AD2\\sysadmin"
- Log access to credential_audit_log
- Display credential in response (since user authorized it)

**Audit:**
- Every credential access logged with:
  - Who (JWT user_id)
  - When (timestamp)
  - What (credential_id, service_name)
  - Why (optional context from work_item)

#### Auto-Tagging

**As work progresses, auto-apply tags:**
- Mention "docker" → tag: docker
- Working on "jupiter" → tag: jupiter
- Client "Dataforth" → tag: dataforth
- Error "connection-timeout" → tag: connection-timeout
- Action "migration" → tag: migration

**Tag categories:**
- technology (docker, apache, mariadb)
- infrastructure (jupiter, pfsense)
- client (dataforth)
- problem_type (ssl-error, connection-timeout)
- action (migration, upgrade, cleanup)

Tags stored in work_item_tags and session_tags junction tables.

#### Context Awareness

**MSP Mode remembers:**
- Previous sessions for this client (last 5)
- Open pending_tasks for this client
- Recent credentials accessed for this client
- Infrastructure topology for this client

**Auto-suggest:**
- "Last time we worked on [client], we left off at [pending_task]"
- "Credentials for [service] are available (last used [date])"
- "Previous similar issue: [problem from history] - same solution?"

---

### Summary: MSP Mode = Smart Auto-Tracking

1. **Auto-categorize** work as it happens (infrastructure, troubleshooting, etc.)
2. **Auto-extract** structured data (commands, files, systems, technologies)
3. **Auto-tag** based on content
4. **Auto-detect** billability
5. **Auto-generate** dense summaries (facts, not narrative)
6. **Auto-link** related data (sessions, problems, credentials)
7. **Minimal user input** required - Claude does the categorization
8. **Maximum information density** - brief but complete

User just works, Claude captures everything in structured, queryable format.

---

### MSP Tool Integrations (Future Capability)

**Core Requirement:** MSP Mode will integrate with external MSP platforms to automate workflows and link session data to ticketing/documentation systems.

#### Planned Integrations

**1. SyncroMSP (PSA/RMM Platform)**
- **Tickets:** Create, update, search, attach files
- **Time Tracking:** Log billable time from sessions automatically
- **Assets:** Link session work to customer assets
- **Contacts:** Associate work with customer contacts
- **API:** REST API with OAuth

**2. MSP Backups (Backup Management)**
- **Reports:** Pull backup status reports
- **Alerts:** Check backup failures
- **Statistics:** Get backup size, success rates
- **Export:** Attach reports to tickets
- **API:** REST API (vendor-specific)

**3. Zapier (Automation Platform)**
- **Triggers:** Session completed → Create ticket
- **Actions:** Ticket created → Start session
- **Webhooks:** Bi-directional automation
- **API:** REST API + webhooks

**4. Future Integrations**
- Autotask (PSA)
- ConnectWise (PSA)
- Datto RMM
- IT Glue (Documentation)
- Microsoft Teams (notifications)
- Email (session summaries)

#### Example Workflow

**User request:**
> "Find the ticket for Dataforth about Backups - update it with the changes we've made today, and pull a report from MSP backups to attach to that ticket"

**Claude MSP Mode execution:**

1. **Identify client context:**
   - Current session: client_id = Dataforth
   - Search tickets: SyncroMSP.search_tickets(customer="Dataforth", subject contains "Backup")

2. **Find ticket:**
   - Result: Ticket #12345 "Backup configuration for NAS"
   - Display to user: "Found ticket #12345: Backup configuration for NAS"

3. **Get today's work:**
   - Query local database: work_items for current session
   - Generate summary:
     ```
     Changes made today:
     - Configured Veeam backup job for D2TESTNAS
     - Set retention: 30 days local, 90 days cloud
     - Tested backup: successful (45GB)
     - Verified restore point creation
     ```

4. **Update ticket:**
   - SyncroMSP.add_comment(ticket_id=12345, comment=summary)
   - Log action: infrastructure_changes linked to external_ticket_id

5. **Pull backup report:**
   - MSPBackups.get_report(customer="Dataforth", date=today)
   - Download report PDF/CSV

6. **Attach to ticket:**
   - SyncroMSP.attach_file(ticket_id=12345, file=report)
   - Confirm to user: "Attached backup report to ticket #12345"

7. **Track integration:**
   - Store in database:
     ```sql
     external_integrations:
       session_id,
       integration_type: 'syncro_ticket',
       external_id: '12345',
       action: 'updated',
       details: JSON(comment, attachments)
     ```

#### Database Schema Additions for Integrations

**New table: `external_integrations`**
```sql
CREATE TABLE external_integrations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id UUID REFERENCES sessions(id) ON DELETE CASCADE,
    work_item_id UUID REFERENCES work_items(id) ON DELETE CASCADE,

    -- Integration details
    integration_type VARCHAR(100) NOT NULL, -- 'syncro_ticket', 'msp_backups', 'zapier_webhook'
    external_id VARCHAR(255), -- ticket ID, asset ID, etc.
    external_url VARCHAR(500), -- direct link to resource

    -- Action tracking
    action VARCHAR(50), -- 'created', 'updated', 'linked', 'attached'
    direction VARCHAR(20), -- 'outbound' (we pushed) or 'inbound' (they triggered)

    -- Data
    request_data TEXT, -- JSON: what we sent
    response_data TEXT, -- JSON: what we received

    -- Metadata
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    created_by VARCHAR(255), -- user who authorized

    INDEX idx_ext_int_session (session_id),
    INDEX idx_ext_int_type (integration_type),
    INDEX idx_ext_int_external (external_id)
);
```

**New table: `integration_credentials`**
```sql
CREATE TABLE integration_credentials (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    integration_name VARCHAR(100) NOT NULL UNIQUE, -- 'syncro', 'msp_backups', 'zapier'

    -- OAuth or API key
    credential_type VARCHAR(50) CHECK(credential_type IN ('oauth', 'api_key', 'basic_auth')),
    api_key_encrypted BYTEA,
    oauth_token_encrypted BYTEA,
    oauth_refresh_token_encrypted BYTEA,
    oauth_expires_at TIMESTAMP,

    -- Endpoints
    api_base_url VARCHAR(500),
    webhook_url VARCHAR(500),

    -- Status
    is_active BOOLEAN DEFAULT true,
    last_tested_at TIMESTAMP,
    last_test_status VARCHAR(50),

    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_int_cred_name (integration_name)
);
```

**New table: `ticket_links`**
```sql
CREATE TABLE ticket_links (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id UUID REFERENCES sessions(id) ON DELETE CASCADE,
    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,

    -- Ticket info
    integration_type VARCHAR(100) NOT NULL, -- 'syncro', 'autotask', 'connectwise'
    ticket_id VARCHAR(255) NOT NULL,
    ticket_number VARCHAR(100), -- human-readable: "T12345"
    ticket_subject VARCHAR(500),
    ticket_url VARCHAR(500),
    ticket_status VARCHAR(100),

    -- Linking
    link_type VARCHAR(50), -- 'related', 'resolves', 'documents'
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_ticket_session (session_id),
    INDEX idx_ticket_client (client_id),
    INDEX idx_ticket_external (integration_type, ticket_id)
);
```

#### API Integration Layer

**FastAPI endpoints for integration management:**
```
GET    /api/v1/integrations                    (list configured integrations)
POST   /api/v1/integrations/{name}/test        (test connection)
GET    /api/v1/integrations/{name}/credentials (get encrypted credentials)
PUT    /api/v1/integrations/{name}/credentials (update credentials)

# Syncro-specific
GET    /api/v1/syncro/tickets                  (search tickets)
POST   /api/v1/syncro/tickets/{id}/comment     (add comment)
POST   /api/v1/syncro/tickets/{id}/attach      (attach file)
POST   /api/v1/syncro/time                     (log time entry)

# MSP Backups
GET    /api/v1/mspbackups/report               (pull report)
GET    /api/v1/mspbackups/status/{client}      (backup status)

# Zapier webhooks
POST   /api/v1/webhooks/zapier                 (receive webhook)
```

#### Workflow Automation

**Session → Ticket Linking:**
When MSP Mode session ends:
1. Ask user: "Link this session to a ticket? (y/n/search)"
2. If search: query Syncro for tickets matching client
3. If found: link session_id to ticket_id in ticket_links table
4. Auto-post session summary as ticket comment (optional)

**Auto Time Tracking:**
When session ends with billable hours:
1. Ask: "Log 2.5 hours to SyncroMSP? (y/n)"
2. If yes: POST to Syncro time tracking API
3. Link time entry ID to session in external_integrations

**Backup Report Automation:**
Trigger: User mentions "backup" in MSP session for client
1. Detect keyword "backup"
2. Auto-suggest: "Pull latest backup report for [Client]? (y/n)"
3. If yes: Query MSPBackups API, display summary
4. Option to attach to ticket or save to session

#### Permission & Security

**OAuth Flow:**
1. User initiates: `/msp integrate syncro`
2. Claude generates OAuth URL, user authorizes in browser
3. Callback URL receives token, encrypts, stores in integration_credentials
4. Refresh token used to maintain access

**API Key Storage:**
- All integration credentials encrypted with AES-256-GCM
- Same master key as credential storage
- Audit log for all integration credential access

**Scopes:**
- Read-only for initial implementation (search tickets, pull reports)
- Write access requires explicit user confirmation per action
- Never auto-update tickets without user approval

#### Future Capabilities

**Natural Language Integration:**
- "Create a ticket for Dataforth about today's backup work"
- "Show me all open tickets for Grabb & Durando"
- "Pull the backup report for last week and email it to [contact]"
- "Log today's 3 hours to ticket T12345"
- "What tickets mention Apache or SSL?"

**Multi-Step Workflows:**
- Session ends → Auto-create ticket → Auto-log time → Auto-attach session summary
- Backup failure detected (via webhook) → Create session → Investigate → Update ticket
- Ticket created in Syncro (webhook) → Notify Claude → Start MSP session

**Bi-Directional Sync:**
- Ticket updated in Syncro → Webhook to Claude → Add to pending_tasks
- Session completed in Claude → Auto-comment in ticket
- Time logged in Claude → Synced to Syncro billing

#### Implementation Priority

**Phase 1 (MVP):**
- Database tables for integrations
- SyncroMSP ticket search and read
- Manual ticket linking
- Session summary → ticket comment (manual)

**Phase 2:**
- MSP Backups report pulling
- File attachments to tickets
- OAuth token refresh automation
- Auto-suggest ticket linking

**Phase 3:**
- Zapier webhook triggers
- Auto time tracking
- Multi-step workflows
- Natural language commands

**Phase 4:**
- Bi-directional sync
- Advanced automation
- Additional PSA integrations (Autotask, ConnectWise)
- IT Glue documentation sync

---

### Impact on Current Architecture

**API Design Considerations:**
- Modular integration layer (plugins per platform)
- Webhook receiver endpoints
- OAuth flow support
- Rate limiting per integration
- Retry logic for failed API calls

**Database Design:**
- external_integrations table (already designed above)
- integration_credentials table (already designed above)
- ticket_links table (already designed above)
- Indexes for fast external_id lookups

**Security:**
- Integration credentials separate from user credentials
- Per-integration permission scopes
- Audit logging for all external API calls
- User confirmation for write operations

**FastAPI Architecture:**
```python
# Integration plugins
integrations/
├── __init__.py
├── base.py              (BaseIntegration abstract class)
├── syncro.py            (SyncroMSP integration)
├── mspbackups.py        (MSP Backups integration)
├── zapier.py            (Zapier webhooks)
└── future/
    ├── autotask.py
    ├── connectwise.py
    └── itglue.py
```

This integration capability is foundational to MSP Mode's value proposition - linking real-world MSP workflows to intelligent automation.

### Normal Mode Behaviors (DEFINED)

**Core Principle:** Track valuable work that doesn't belong to a specific client or dev project. General research, internal tasks, exploration, learning.

#### Purpose

Normal Mode is for:
- **Research and exploration** - "How does JWT authentication work?"
- **General questions** - "What's the best way to handle SQL migrations?"
- **Internal infrastructure** (non-client) - Working on Jupiter/Saturn without client context
- **Learning/experimentation** - Testing new tools, trying approaches
- **Documentation** - Writing guides, updating READMEs
- **Non-categorized work** - Anything that doesn't fit MSP or Dev

**Not for:**
- Client work → Use MSP Mode
- Specific development projects → Use Dev Mode

#### When `/normal` is Called

1. **Mode Switch:**
   - If coming from MSP/Dev mode: preserve all knowledge/context from previous mode
   - Set session context to "general" (no client_id, no project_id)
   - Display: "Normal Mode | General work session"

2. **Knowledge Retention:**
   - **Keep:** All learned information, credentials accessed, context from previous modes
   - **Clear:** Client/project assignment only
   - **Rationale:** You might research something in Normal mode, then apply it in MSP mode

3. **Session Creation:**
   - Create session with:
     - client_id = NULL
     - project_id = NULL (or link to "Internal" or "Research" pseudo-project)
     - session_title = "General work session: [auto-generated from topic]"
     - is_billable = false (by default, since not client work)

#### During Normal Mode Session

**Tracking (lighter than MSP):**
- Still create work_items, but less granular
- Track major actions: "Researched FastAPI authentication patterns"
- Track useful findings: "Found pyjwt library, better than python-jose"
- Track decisions: "Decided to use Alembic for migrations"

**What gets stored:**
- Work items with category (usually 'documentation', 'research', or 'development')
- Key commands run (if applicable)
- Files modified (if applicable)
- Tags: technology, topics
- Brief summary of what was learned/accomplished

**What doesn't get stored:**
- Less emphasis on billability tracking
- No client/project relationships
- Less detailed command/file tracking (unless requested)

#### Information Density in Normal Mode

**Focus on:** Capturing useful knowledge, decisions, findings

**Example Normal Mode work_item:**
```
Title: Researched PostgreSQL connection pooling for FastAPI
Category: research
Description: Compared SQLAlchemy pooling vs asyncpg.
Finding: SQLAlchemy pool_size=20, max_overflow=10 optimal for our load.
Decision: Use SQLAlchemy with pool_pre_ping=True for connection health checks.
Tags: postgresql, sqlalchemy, fastapi, connection-pooling
Billable: false
```

**Not:** "I started by searching for PostgreSQL connection pooling documentation..."

#### Session End (Normal Mode)

**Auto-save:**
- session.client_id = NULL
- session.is_billable = false
- session.summary = brief summary of research/work done

**Generated summary example:**
```
Session: General Research
Duration: 45 minutes
Category: Research

Topics Explored:
- FastAPI database connection pooling
- JWT vs session authentication
- Alembic migration strategies

Key Findings:
- SQLAlchemy pooling recommended for our use case
- JWT refresh tokens better than long-lived access tokens
- Alembic supports auto-generate from models

Tags: fastapi, postgresql, jwt, alembic
Billable: No
```

#### Value of Normal Mode Sessions

**Why track this?**
1. **Knowledge base** - "What did I learn about X last month?"
2. **Decision trail** - "Why did we choose technology Y?"
3. **Reference** - "Where did I see that solution before?"
4. **Context recovery** - Future Claude instances can search: "Show me research on JWT authentication"

**Queryable:**
- "What have I researched about Docker networking?"
- "When did I decide to use FastAPI over Flask?"
- "Show all sessions tagged 'postgresql'"

#### Mode Comparison

| Aspect | MSP Mode | Dev Mode | Normal Mode |
|--------|----------|----------|-------------|
| **Purpose** | Client work | Specific projects | General work/research |
| **Client/Project** | Required | Optional | None (NULL) |
| **Billable** | Default: yes | Default: no | Default: no |
| **Detail Level** | High (every command) | Medium | Light (key actions) |
| **Focus** | Client value delivery | Code/features | Knowledge/learning |
| **Session Title** | "[Client] - [Issue]" | "[Project] - [Feature]" | "Research: [Topic]" |

#### Switching Between Modes

**MSP → Normal:**
- User: "Let me research how to fix this SSL issue"
- `/normal` → Research mode, but can reference client context if needed
- Knowledge retained: knows which client, what SSL issue
- Categorization: research session, not billable client work

**Normal → MSP:**
- User: "Okay, back to Dataforth"
- `/msp` → Resumes (or starts new) Dataforth session
- Knowledge retained: knows solution from research
- Categorization: client work, billable

**Dev → Normal → MSP:**
- Modes are fluid, knowledge carries through
- Only categorization changes
- Session assignments change (project vs client vs general)

---

### Development Mode Behaviors (To Define)
1. What should Development Mode track?
2. How does it differ from MSP and Normal modes?
3. Integration with git repos?

### Implementation Order
1. Database schema design
2. API development and deployment
3. MCP server or API client for Claude Code
4. MSP Mode slash command
5. Development Mode
6. Normal Mode

---

## Security Considerations

### Credential Storage
- **Never store plaintext passwords**
- Use Fernet encryption or AES-256-GCM
- Encryption key stored separately from database
- Key rotation strategy needed

### API Security
- HTTPS only (no HTTP)
- Rate limiting (prevent brute force)
- IP whitelisting (optional - VPN only)
- Token expiration and refresh
- Revocation list for compromised tokens
- Audit logging for credential access

### Multi-Machine Sync
- Encrypted tokens in Gitea config
- git-crypt or encrypted JSON values
- Never commit plaintext tokens to repo

---

## Next Steps (Planning Phase)

1. ✅ Architecture decisions (SQL, FastAPI, JWT)
2. ⏳ Define MSP Mode behaviors in detail
3. ⏳ Design database schema
4. ⏳ Define API endpoints specification
5. ⏳ Create authentication flow diagram
6. ⏳ Design slash command interactions

---

## Notes

- This spec will evolve as we discuss details
- Focus on scalability and robustness
- Design for future team members and integrations
- All decisions documented with rationale

---

## Change Log

**2026-01-15:**
- Initial spec created
- Architecture decisions: SQL, FastAPI, JWT, Gitea config
- Technology stack defined
- High-level infrastructure design
- Open questions identified
- **Database schema designed:** 27 tables via parallel agent analysis
  - 5 parallel agents analyzed: sessions, credentials, projects, work categorization, infrastructure
  - Comprehensive schema with 22 core tables + 5 junction tables
  - Analyzed 37 session logs, credentials file, all projects, infrastructure docs
  - Estimated storage: 1-2 GB/year
  - Pre-identified 157+ tags for categorization
- **MSP Mode behaviors defined:**
  - Auto-categorization of client work
  - Information-dense storage format
  - Auto-tracking: commands, files, problems, credentials, infrastructure changes
  - Smart billability detection
  - Context awareness and auto-suggestion
- **Normal Mode behaviors defined:**
  - For general work/research not assigned to client or dev project
  - Knowledge retention across mode switches
  - Lighter tracking than MSP mode
  - Captures decisions, findings, learnings
  - Queryable knowledge base