The file D:\ClaudeTools\MSP-MODE-SPEC.md has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
   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:** 22 core tables + 5 junction tables = **27 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→
   305→---
   306→
   307→### 1. Core MSP Tracking Tables (5 tables)
   308→
   309→#### `clients`
   310→Master table for all client organizations.
   311→
   312→```sql
   313→CREATE TABLE clients (
   314→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   315→    name VARCHAR(255) NOT NULL UNIQUE,
   316→    type VARCHAR(50) NOT NULL CHECK(type IN ('msp_client', 'internal', 'project')),
   317→    network_subnet VARCHAR(100), -- e.g., "192.168.0.0/24"
   318→    domain_name VARCHAR(255), -- AD domain or primary domain
   319→    m365_tenant_id UUID, -- Microsoft 365 tenant ID
   320→    primary_contact VARCHAR(255),
   321→    notes TEXT,
   322→    is_active BOOLEAN DEFAULT true,
   323→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   324→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   325→
   326→    INDEX idx_clients_type (type),
   327→    INDEX idx_clients_name (name)
   328→);
   329→```
   330→
   331→**Examples:** Dataforth, Grabb & Durando, Valley Wide Plastering, AZ Computer Guru (internal)
   332→
   333→---
   334→
   335→#### `projects`
   336→Individual projects/engagements for clients.
   337→
   338→```sql
   339→CREATE TABLE projects (
   340→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   341→    client_id UUID NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
   342→    name VARCHAR(255) NOT NULL,
   343→    slug VARCHAR(255) UNIQUE, -- directory name: "dataforth-dos"
   344→    category VARCHAR(50) CHECK(category IN (
   345→        'client_project', 'internal_product', 'infrastructure',
   346→        'website', 'development_tool', 'documentation'
   347→    )),
   348→    status VARCHAR(50) DEFAULT 'working' CHECK(status IN (
   349→        'complete', 'working', 'blocked', 'pending', 'critical', 'deferred'
   350→    )),
   351→    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
   352→    description TEXT,
   353→    started_date DATE,
   354→    target_completion_date DATE,
   355→    completed_date DATE,
   356→    estimated_hours DECIMAL(10,2),
   357→    actual_hours DECIMAL(10,2),
   358→    gitea_repo_url VARCHAR(500),
   359→    notes TEXT,
   360→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   361→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   362→
   363→    INDEX idx_projects_client (client_id),
   364→    INDEX idx_projects_status (status),
   365→    INDEX idx_projects_slug (slug)
   366→);
   367→```
   368→
   369→**Examples:** dataforth-dos, gururmm, grabb-website-move
   370→
   371→---
   372→
   373→#### `sessions`
   374→Work sessions with time tracking.
   375→
   376→```sql
   377→CREATE TABLE sessions (
   378→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   379→    client_id UUID REFERENCES clients(id) ON DELETE SET NULL,
   380→    project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
   381→    session_date DATE NOT NULL,
   382→    start_time TIMESTAMP,
   383→    end_time TIMESTAMP,
   384→    duration_minutes INTEGER, -- auto-calculated or manual
   385→    status VARCHAR(50) DEFAULT 'completed' CHECK(status IN (
   386→        'completed', 'in_progress', 'blocked', 'pending'
   387→    )),
   388→    session_title VARCHAR(500) NOT NULL,
   389→    summary TEXT, -- markdown summary
   390→    is_billable BOOLEAN DEFAULT false,
   391→    billable_hours DECIMAL(10,2),
   392→    technician VARCHAR(255), -- "Mike Swanson", etc.
   393→    session_log_file VARCHAR(500), -- path to .md file
   394→    notes TEXT,
   395→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   396→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   397→
   398→    INDEX idx_sessions_client (client_id),
   399→    INDEX idx_sessions_project (project_id),
   400→    INDEX idx_sessions_date (session_date),
   401→    INDEX idx_sessions_billable (is_billable)
   402→);
   403→```
   404→
   405→---
   406→
   407→#### `work_items`
   408→Individual tasks/actions within sessions (granular tracking).
   409→
   410→```sql
   411→CREATE TABLE work_items (
   412→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   413→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   414→    category VARCHAR(50) NOT NULL CHECK(category IN (
   415→        'infrastructure', 'troubleshooting', 'configuration',
   416→        'development', 'maintenance', 'security', 'documentation'
   417→    )),
   418→    title VARCHAR(500) NOT NULL,
   419→    description TEXT NOT NULL,
   420→    status VARCHAR(50) DEFAULT 'completed' CHECK(status IN (
   421→        'completed', 'in_progress', 'blocked', 'pending', 'deferred'
   422→    )),
   423→    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
   424→    is_billable BOOLEAN DEFAULT false,
   425→    estimated_minutes INTEGER,
   426→    actual_minutes INTEGER,
   427→    affected_systems TEXT, -- JSON array: ["jupiter", "172.16.3.20"]
   428→    technologies_used TEXT, -- JSON array: ["docker", "mariadb"]
   429→    item_order INTEGER, -- sequence within session
   430→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   431→    completed_at TIMESTAMP,
   432→
   433→    INDEX idx_work_items_session (session_id),
   434→    INDEX idx_work_items_category (category),
   435→    INDEX idx_work_items_status (status)
   436→);
   437→```
   438→
   439→**Categories distribution (from analysis):**
   440→- Infrastructure: 30%
   441→- Troubleshooting: 25%
   442→- Configuration: 15%
   443→- Development: 15%
   444→- Maintenance: 10%
   445→- Security: 5%
   446→
   447→---
   448→
   449→#### `pending_tasks`
   450→Open items across all clients/projects.
   451→
   452→```sql
   453→CREATE TABLE pending_tasks (
   454→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   455→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   456→    project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
   457→    work_item_id UUID REFERENCES work_items(id) ON DELETE SET NULL,
   458→    title VARCHAR(500) NOT NULL,
   459→    description TEXT,
   460→    priority VARCHAR(20) CHECK(priority IN ('critical', 'high', 'medium', 'low')),
   461→    blocked_by TEXT, -- what's blocking this
   462→    assigned_to VARCHAR(255),
   463→    due_date DATE,
   464→    status VARCHAR(50) DEFAULT 'pending' CHECK(status IN (
   465→        'pending', 'in_progress', 'blocked', 'completed', 'cancelled'
   466→    )),
   467→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   468→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   469→    completed_at TIMESTAMP,
   470→
   471→    INDEX idx_pending_tasks_client (client_id),
   472→    INDEX idx_pending_tasks_status (status),
   473→    INDEX idx_pending_tasks_priority (priority)
   474→);
   475→```
   476→
   477→---
   478→
   479→### 2. Client & Infrastructure Tables (7 tables)
   480→
   481→#### `sites`
   482→Physical/logical locations for clients.
   483→
   484→```sql
   485→CREATE TABLE sites (
   486→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   487→    client_id UUID NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
   488→    name VARCHAR(255) NOT NULL, -- "Main Office", "SLC - Salt Lake City"
   489→    network_subnet VARCHAR(100), -- "172.16.9.0/24"
   490→    vpn_required BOOLEAN DEFAULT false,
   491→    vpn_subnet VARCHAR(100), -- "192.168.1.0/24"
   492→    gateway_ip VARCHAR(45), -- IPv4/IPv6
   493→    dns_servers TEXT, -- JSON array
   494→    notes TEXT,
   495→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   496→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   497→
   498→    INDEX idx_sites_client (client_id)
   499→);
   500→```
   501→
   502→---
   503→
   504→#### `infrastructure`
   505→Servers, network devices, NAS, workstations.
   506→
   507→```sql
   508→CREATE TABLE infrastructure (
   509→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   510→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   511→    site_id UUID REFERENCES sites(id) ON DELETE SET NULL,
   512→    asset_type VARCHAR(50) NOT NULL CHECK(asset_type IN (
   513→        'physical_server', 'virtual_machine', 'container',
   514→        'network_device', 'nas_storage', 'workstation',
   515→        'firewall', 'domain_controller'
   516→    )),
   517→    hostname VARCHAR(255) NOT NULL,
   518→    ip_address VARCHAR(45),
   519→    mac_address VARCHAR(17),
   520→    os VARCHAR(255), -- "Ubuntu 22.04", "Windows Server 2022", "Unraid"
   521→    role_description TEXT, -- "Primary DC, NPS/RADIUS server"
   522→    parent_host_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL, -- for VMs/containers
   523→    status VARCHAR(50) DEFAULT 'active' CHECK(status IN (
   524→        'active', 'migration_source', 'migration_destination', 'decommissioned'
   525→    )),
   526→    notes TEXT,
   527→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   528→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   529→
   530→    INDEX idx_infrastructure_client (client_id),
   531→    INDEX idx_infrastructure_type (asset_type),
   532→    INDEX idx_infrastructure_hostname (hostname),
   533→    INDEX idx_infrastructure_parent (parent_host_id)
   534→);
   535→```
   536→
   537→**Examples:** Jupiter, AD2 (Dataforth), VWP-DC1, D2TESTNAS
   538→
   539→---
   540→
   541→#### `services`
   542→Applications/services running on infrastructure.
   543→
   544→```sql
   545→CREATE TABLE services (
   546→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   547→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
   548→    service_name VARCHAR(255) NOT NULL, -- "Gitea", "PostgreSQL", "Apache"
   549→    service_type VARCHAR(100), -- "git_hosting", "database", "web_server"
   550→    external_url VARCHAR(500), -- "https://git.azcomputerguru.com"
   551→    internal_url VARCHAR(500), -- "http://172.16.3.20:3000"
   552→    port INTEGER,
   553→    protocol VARCHAR(50), -- "https", "ssh", "smb"
   554→    status VARCHAR(50) DEFAULT 'running' CHECK(status IN (
   555→        'running', 'stopped', 'error', 'maintenance'
   556→    )),
   557→    version VARCHAR(100),
   558→    notes TEXT,
   559→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   560→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   561→
   562→    INDEX idx_services_infrastructure (infrastructure_id),
   563→    INDEX idx_services_name (service_name),
   564→    INDEX idx_services_type (service_type)
   565→);
   566→```
   567→
   568→---
   569→
   570→#### `service_relationships`
   571→Dependencies and relationships between services.
   572→
   573→```sql
   574→CREATE TABLE service_relationships (
   575→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   576→    from_service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
   577→    to_service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
   578→    relationship_type VARCHAR(50) NOT NULL CHECK(relationship_type IN (
   579→        'hosted_on', 'proxied_by', 'authenticates_via',
   580→        'backend_for', 'depends_on', 'replicates_to'
   581→    )),
   582→    notes TEXT,
   583→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   584→
   585→    UNIQUE(from_service_id, to_service_id, relationship_type),
   586→    INDEX idx_service_rel_from (from_service_id),
   587→    INDEX idx_service_rel_to (to_service_id)
   588→);
   589→```
   590→
   591→**Examples:**
   592→- Gitea (proxied_by) NPM
   593→- GuruRMM API (hosted_on) Jupiter container
   594→
   595→---
   596→
   597→#### `networks`
   598→Network segments, VLANs, VPN networks.
   599→
   600→```sql
   601→CREATE TABLE networks (
   602→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   603→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   604→    site_id UUID REFERENCES sites(id) ON DELETE CASCADE,
   605→    network_name VARCHAR(255) NOT NULL,
   606→    network_type VARCHAR(50) CHECK(network_type IN (
   607→        'lan', 'vpn', 'vlan', 'isolated', 'dmz'
   608→    )),
   609→    cidr VARCHAR(100) NOT NULL, -- "192.168.0.0/24"
   610→    gateway_ip VARCHAR(45),
   611→    vlan_id INTEGER,
   612→    notes TEXT,
   613→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   614→
   615→    INDEX idx_networks_client (client_id),
   616→    INDEX idx_networks_site (site_id)
   617→);
   618→```
   619→
   620→---
   621→
   622→#### `firewall_rules`
   623→Network security rules (for documentation/audit trail).
   624→
   625→```sql
   626→CREATE TABLE firewall_rules (
   627→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   628→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
   629→    rule_name VARCHAR(255),
   630→    source_cidr VARCHAR(100),
   631→    destination_cidr VARCHAR(100),
   632→    port INTEGER,
   633→    protocol VARCHAR(20), -- "tcp", "udp", "icmp"
   634→    action VARCHAR(20) CHECK(action IN ('allow', 'deny', 'drop')),
   635→    rule_order INTEGER,
   636→    notes TEXT,
   637→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   638→    created_by VARCHAR(255),
   639→
   640→    INDEX idx_firewall_infra (infrastructure_id)
   641→);
   642→```
   643→
   644→---
   645→
   646→#### `m365_tenants`
   647→Microsoft 365 tenant tracking.
   648→
   649→```sql
   650→CREATE TABLE m365_tenants (
   651→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   652→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   653→    tenant_id UUID NOT NULL UNIQUE, -- Microsoft tenant ID
   654→    tenant_name VARCHAR(255), -- "dataforth.com"
   655→    default_domain VARCHAR(255), -- "dataforthcorp.onmicrosoft.com"
   656→    admin_email VARCHAR(255),
   657→    cipp_name VARCHAR(255), -- name in CIPP portal
   658→    notes TEXT,
   659→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   660→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   661→
   662→    INDEX idx_m365_client (client_id),
   663→    INDEX idx_m365_tenant_id (tenant_id)
   664→);
   665→```
   666→
   667→---
   668→
   669→### 3. Credentials & Security Tables (4 tables)
   670→
   671→#### `credentials`
   672→Encrypted credential storage (values encrypted at rest).
   673→
   674→```sql
   675→CREATE TABLE credentials (
   676→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   677→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   678→    service_id UUID REFERENCES services(id) ON DELETE CASCADE,
   679→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE CASCADE,
   680→    credential_type VARCHAR(50) NOT NULL CHECK(credential_type IN (
   681→        'password', 'api_key', 'oauth', 'ssh_key',
   682→        'shared_secret', 'jwt', 'connection_string', 'certificate'
   683→    )),
   684→    service_name VARCHAR(255) NOT NULL, -- "Gitea Admin", "AD2 sysadmin"
   685→    username VARCHAR(255),
   686→    password_encrypted BYTEA, -- AES-256-GCM encrypted
   687→    api_key_encrypted BYTEA,
   688→    client_id_oauth VARCHAR(255), -- for OAuth
   689→    client_secret_encrypted BYTEA,
   690→    tenant_id_oauth VARCHAR(255),
   691→    public_key TEXT, -- for SSH
   692→    token_encrypted BYTEA,
   693→    connection_string_encrypted BYTEA,
   694→    integration_code VARCHAR(255), -- for services like Autotask
   695→
   696→    -- Metadata
   697→    external_url VARCHAR(500),
   698→    internal_url VARCHAR(500),
   699→    custom_port INTEGER,
   700→    role_description VARCHAR(500),
   701→    requires_vpn BOOLEAN DEFAULT false,
   702→    requires_2fa BOOLEAN DEFAULT false,
   703→    ssh_key_auth_enabled BOOLEAN DEFAULT false,
   704→    access_level VARCHAR(100),
   705→
   706→    -- Lifecycle
   707→    expires_at TIMESTAMP,
   708→    last_rotated_at TIMESTAMP,
   709→    is_active BOOLEAN DEFAULT true,
   710→
   711→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   712→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   713→
   714→    INDEX idx_credentials_client (client_id),
   715→    INDEX idx_credentials_service (service_id),
   716→    INDEX idx_credentials_type (credential_type),
   717→    INDEX idx_credentials_active (is_active)
   718→);
   719→```
   720→
   721→**Security:**
   722→- All sensitive fields encrypted with AES-256-GCM
   723→- Encryption key stored separately (environment variable or vault)
   724→- Master password unlock mechanism
   725→
   726→---
   727→
   728→#### `credential_audit_log`
   729→Audit trail for credential access.
   730→
   731→```sql
   732→CREATE TABLE credential_audit_log (
   733→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   734→    credential_id UUID NOT NULL REFERENCES credentials(id) ON DELETE CASCADE,
   735→    action VARCHAR(50) NOT NULL CHECK(action IN (
   736→        'view', 'create', 'update', 'delete', 'rotate', 'decrypt'
   737→    )),
   738→    user_id VARCHAR(255) NOT NULL, -- JWT sub claim
   739→    ip_address VARCHAR(45),
   740→    user_agent TEXT,
   741→    details TEXT, -- JSON: what changed, why
   742→    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   743→
   744→    INDEX idx_cred_audit_credential (credential_id),
   745→    INDEX idx_cred_audit_user (user_id),
   746→    INDEX idx_cred_audit_timestamp (timestamp)
   747→);
   748→```
   749→
   750→---
   751→
   752→#### `security_incidents`
   753→Track security events and remediation.
   754→
   755→```sql
   756→CREATE TABLE security_incidents (
   757→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   758→    client_id UUID REFERENCES clients(id) ON DELETE CASCADE,
   759→    service_id UUID REFERENCES services(id) ON DELETE SET NULL,
   760→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
   761→    incident_type VARCHAR(100) CHECK(incident_type IN (
   762→        'bec', 'backdoor', 'malware', 'unauthorized_access',
   763→        'data_breach', 'phishing', 'ransomware', 'brute_force'
   764→    )),
   765→    incident_date TIMESTAMP NOT NULL,
   766→    severity VARCHAR(50) CHECK(severity IN ('critical', 'high', 'medium', 'low')),
   767→    description TEXT NOT NULL,
   768→    findings TEXT, -- investigation results
   769→    remediation_steps TEXT,
   770→    status VARCHAR(50) DEFAULT 'investigating' CHECK(status IN (
   771→        'investigating', 'contained', 'resolved', 'monitoring'
   772→    )),
   773→    resolved_at TIMESTAMP,
   774→    notes TEXT,
   775→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   776→    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   777→
   778→    INDEX idx_incidents_client (client_id),
   779→    INDEX idx_incidents_type (incident_type),
   780→    INDEX idx_incidents_status (status)
   781→);
   782→```
   783→
   784→**Examples:** BG Builders OAuth backdoor, CW Concrete BEC
   785→
   786→---
   787→
   788→#### `credential_permissions`
   789→Access control for credentials (future team expansion).
   790→
   791→```sql
   792→CREATE TABLE credential_permissions (
   793→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   794→    credential_id UUID NOT NULL REFERENCES credentials(id) ON DELETE CASCADE,
   795→    user_id VARCHAR(255) NOT NULL, -- or role_id
   796→    permission_level VARCHAR(50) CHECK(permission_level IN ('read', 'write', 'admin')),
   797→    granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   798→    granted_by VARCHAR(255),
   799→
   800→    UNIQUE(credential_id, user_id),
   801→    INDEX idx_cred_perm_credential (credential_id),
   802→    INDEX idx_cred_perm_user (user_id)
   803→);
   804→```
   805→
   806→---
   807→
   808→### 4. Work Details Tables (6 tables)
   809→
   810→#### `file_changes`
   811→Track files created/modified/deleted during sessions.
   812→
   813→```sql
   814→CREATE TABLE file_changes (
   815→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   816→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   817→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   818→    file_path VARCHAR(1000) NOT NULL,
   819→    change_type VARCHAR(50) CHECK(change_type IN (
   820→        'created', 'modified', 'deleted', 'renamed', 'backed_up'
   821→    )),
   822→    backup_path VARCHAR(1000),
   823→    size_bytes BIGINT,
   824→    description TEXT,
   825→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   826→
   827→    INDEX idx_file_changes_work_item (work_item_id),
   828→    INDEX idx_file_changes_session (session_id)
   829→);
   830→```
   831→
   832→---
   833→
   834→#### `commands_run`
   835→Shell/PowerShell/SQL commands executed.
   836→
   837→```sql
   838→CREATE TABLE commands_run (
   839→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   840→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   841→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   842→    command_text TEXT NOT NULL,
   843→    host VARCHAR(255), -- where executed: "jupiter", "172.16.3.20"
   844→    shell_type VARCHAR(50), -- "bash", "powershell", "sql", "docker"
   845→    success BOOLEAN,
   846→    output_summary TEXT, -- first/last lines or error
   847→    execution_order INTEGER, -- sequence within work item
   848→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   849→
   850→    INDEX idx_commands_work_item (work_item_id),
   851→    INDEX idx_commands_session (session_id),
   852→    INDEX idx_commands_host (host)
   853→);
   854→```
   855→
   856→---
   857→
   858→#### `infrastructure_changes`
   859→Audit trail for infrastructure modifications.
   860→
   861→```sql
   862→CREATE TABLE infrastructure_changes (
   863→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   864→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   865→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   866→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
   867→    change_type VARCHAR(50) CHECK(change_type IN (
   868→        'dns', 'firewall', 'routing', 'ssl', 'container',
   869→        'service_config', 'hardware', 'network', 'storage'
   870→    )),
   871→    target_system VARCHAR(255) NOT NULL,
   872→    before_state TEXT,
   873→    after_state TEXT,
   874→    is_permanent BOOLEAN DEFAULT true,
   875→    rollback_procedure TEXT,
   876→    verification_performed BOOLEAN DEFAULT false,
   877→    verification_notes TEXT,
   878→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   879→
   880→    INDEX idx_infra_changes_work_item (work_item_id),
   881→    INDEX idx_infra_changes_session (session_id),
   882→    INDEX idx_infra_changes_infrastructure (infrastructure_id)
   883→);
   884→```
   885→
   886→---
   887→
   888→#### `problem_solutions`
   889→Issue tracking with root cause and resolution.
   890→
   891→```sql
   892→CREATE TABLE problem_solutions (
   893→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   894→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   895→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   896→    problem_description TEXT NOT NULL,
   897→    symptom TEXT, -- what user saw
   898→    error_message TEXT, -- exact error code/message
   899→    investigation_steps TEXT, -- JSON array of diagnostic commands
   900→    root_cause TEXT,
   901→    solution_applied TEXT NOT NULL,
   902→    verification_method TEXT,
   903→    rollback_plan TEXT,
   904→    recurrence_count INTEGER DEFAULT 1, -- if same problem reoccurs
   905→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   906→
   907→    INDEX idx_problems_work_item (work_item_id),
   908→    INDEX idx_problems_session (session_id)
   909→);
   910→```
   911→
   912→---
   913→
   914→#### `deployments`
   915→Track software/config deployments.
   916→
   917→```sql
   918→CREATE TABLE deployments (
   919→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   920→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   921→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   922→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
   923→    service_id UUID REFERENCES services(id) ON DELETE SET NULL,
   924→    deployment_type VARCHAR(50) CHECK(deployment_type IN (
   925→        'code', 'config', 'database', 'container', 'service_restart'
   926→    )),
   927→    version VARCHAR(100),
   928→    description TEXT,
   929→    deployed_from VARCHAR(500), -- source path or repo
   930→    deployed_to VARCHAR(500), -- destination
   931→    rollback_available BOOLEAN DEFAULT false,
   932→    rollback_procedure TEXT,
   933→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   934→
   935→    INDEX idx_deployments_work_item (work_item_id),
   936→    INDEX idx_deployments_infrastructure (infrastructure_id),
   937→    INDEX idx_deployments_service (service_id)
   938→);
   939→```
   940→
   941→---
   942→
   943→#### `database_changes`
   944→Track database schema/data modifications.
   945→
   946→```sql
   947→CREATE TABLE database_changes (
   948→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   949→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
   950→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
   951→    database_name VARCHAR(255) NOT NULL,
   952→    infrastructure_id UUID REFERENCES infrastructure(id) ON DELETE SET NULL,
   953→    change_type VARCHAR(50) CHECK(change_type IN (
   954→        'schema', 'data', 'index', 'optimization', 'cleanup', 'migration'
   955→    )),
   956→    sql_executed TEXT,
   957→    rows_affected BIGINT,
   958→    size_freed_bytes BIGINT, -- for cleanup operations
   959→    backup_taken BOOLEAN DEFAULT false,
   960→    backup_location VARCHAR(500),
   961→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   962→
   963→    INDEX idx_db_changes_work_item (work_item_id),
   964→    INDEX idx_db_changes_database (database_name)
   965→);
   966→```
   967→
   968→---
   969→
   970→### 5. Tagging & Categorization Tables (3 tables)
   971→
   972→#### `tags`
   973→Flexible tagging system for work items.
   974→
   975→```sql
   976→CREATE TABLE tags (
   977→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
   978→    name VARCHAR(100) UNIQUE NOT NULL,
   979→    category VARCHAR(50) CHECK(category IN (
   980→        'technology', 'client', 'infrastructure',
   981→        'problem_type', 'action', 'service'
   982→    )),
   983→    description TEXT,
   984→    usage_count INTEGER DEFAULT 0, -- auto-increment on use
   985→    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   986→
   987→    INDEX idx_tags_category (category),
   988→    INDEX idx_tags_name (name)
   989→);
   990→```
   991→
   992→**Pre-populated tags:** 157+ tags identified from analysis
   993→- 58 technology tags (docker, postgresql, apache, etc.)
   994→- 24 infrastructure tags (jupiter, saturn, pfsense, etc.)
   995→- 20+ client tags
   996→- 30 problem type tags (connection-timeout, ssl-error, etc.)
   997→- 25 action tags (migration, upgrade, cleanup, etc.)
   998→
   999→---
  1000→
  1001→#### `work_item_tags` (Junction Table)
  1002→Many-to-many relationship: work items ↔ tags.
  1003→
  1004→```sql
  1005→CREATE TABLE work_item_tags (
  1006→    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,
  1007→    tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
  1008→    PRIMARY KEY (work_item_id, tag_id),
  1009→
  1010→    INDEX idx_wit_work_item (work_item_id),
  1011→    INDEX idx_wit_tag (tag_id)
  1012→);
  1013→```
  1014→
  1015→---
  1016→
  1017→#### `session_tags` (Junction Table)
  1018→Many-to-many relationship: sessions ↔ tags.
  1019→
  1020→```sql
  1021→CREATE TABLE session_tags (
  1022→    session_id UUID NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
  1023→    tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
  1024→    PRIMARY KEY (session_id, tag_id),
  1025→
  1026→    INDEX idx_st_session (session_id),
  1027→    INDEX idx_st_tag (tag_id)
  1028→);
  1029→```
  1030→
  1031→---
  1032→
  1033→### 6. System & Audit Tables (2 tables)
  1034→
  1035→#### `api_audit_log`
  1036→Track all API requests for security and debugging.
  1037→
  1038→```sql
  1039→CREATE TABLE api_audit_log (
  1040→    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  1041→    user_id VARCHAR(255) NOT NULL, -- JWT sub claim
  1042→    endpoint VARCHAR(500) NOT NULL, -- "/api/v1/sessions"
  1043→    http_method VARCHAR(10), -- GET, POST, PUT, DELETE
  1044→    ip_address VARCHAR(45),
  1045→    user_agent TEXT,
  1046→    request_body TEXT, -- sanitized (no credentials)
  1047→    response_status INTEGER, -- 200, 401, 500
  1048→    response_time_ms INTEGER,
  1049→    error_message TEXT,
  1050→    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  1051→
  1052→    INDEX idx_api_audit_user (user_id),
  1053→    INDEX idx_api_audit_endpoint (endpoint),
  1054→    INDEX idx_api_audit_timestamp (timestamp),
  1055→    INDEX idx_api_audit_status (response_status)
  1056→);
  1057→```
  1058→
  1059→---
  1060→
  1061→#### `schema_migrations`
  1062→Track database schema versions (Alembic migrations).
  1063→
  1064→```sql
  1065→CREATE TABLE schema_migrations (
  1066→    version_id VARCHAR(100) PRIMARY KEY,
  1067→    description TEXT,
  1068→    applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  1069→    applied_by VARCHAR(255),
  1070→    migration_sql TEXT
  1071→);
  1072→```
  1073→
  1074→---
  1075→
  1076→### Junction Tables Summary
  1077→
  1078→1. **work_item_tags** - Work items ↔ Tags
  1079→2. **session_tags** - Sessions ↔ Tags
  1080→3. **project_relationships** (optional) - Projects ↔ Projects (related/dependent)
  1081→4. **project_session_logs** (optional) - Projects ↔ Sessions (many-to-many if sessions span multiple projects)
  1082→
  1083→---
  1084→
  1085→### Schema Statistics
  1086→
  1087→**Total Tables:** 27
  1088→- Core MSP: 5 tables
  1089→- Client & Infrastructure: 7 tables
  1090→- Credentials & Security: 4 tables
  1091→- Work Details: 6 tables
  1092→- Tagging: 3 tables (+ 2 junction)
  1093→- System: 2 tables
  1094→
  1095→**Estimated Row Counts (1 year of MSP work):**
  1096→- sessions: ~500-1000 (2-3 per day)
  1097→- work_items: ~5,000-10,000 (5-10 per session)
  1098→- file_changes: ~10,000
  1099→- commands_run: ~20,000
  1100→- tags: ~200
  1101→- clients: ~50
  1102→- projects: ~100
  1103→- credentials: ~500
  1104→- api_audit_log: ~100,000+
  1105→
  1106→**Storage Estimate:** ~1-2 GB per year (compressed)
  1107→
  1108→---
  1109→
  1110→### Design Principles Applied
  1111→
  1112→1. **Normalized Structure** - Minimizes data duplication
  1113→2. **Flexible Tagging** - Supports evolving categorization
  1114→3. **Audit Trail** - Comprehensive logging for security and troubleshooting
  1115→4. **Scalability** - Designed for multi-user MSP team growth
  1116→5. **Security First** - Encrypted credentials, access control, audit logging
  1117→6. **Temporal Tracking** - created_at, updated_at, completed_at timestamps
  1118→7. **Soft Deletes** - is_active flags allow recovery
  1119→8. **Relationships** - Foreign keys enforce referential integrity
  1120→9. **Indexes** - Strategic indexes for common query patterns
  1121→10. **JSON Flexibility** - JSON fields for arrays/flexible data (affected_systems, technologies_used)
  1122→
  1123→---
  1124→
  1125→## Next Steps for Database Implementation
  1126→
  1127→1. ✅ Schema designed (27 tables, relationships defined)
  1128→2. ⏳ Create Alembic migration files
  1129→3. ⏳ Set up encryption key management
  1130→4. ⏳ Seed initial data (tags, MSP infrastructure)
  1131→5. ⏳ Create database on Jupiter MariaDB
  1132→6. ⏳ Build FastAPI models (SQLAlchemy + Pydantic)
  1133→7. ⏳ Implement API endpoints
  1134→8. ⏳ Create authentication flow
  1135→9. ⏳ Build MSP Mode slash command integration
  1136→
  1137→---
  1138→
  1139→## Open Questions
  1140→
  1141→### MSP Mode Behaviors (To Define)
  1142→1. What should happen when `/msp` is called?
  1143→   - Prompt for client selection?

---

## 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:** 22 core tables + 5 junction tables = **27 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)

---

### 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:** 27
- Core MSP: 5 tables
- Client & Infrastructure: 7 tables
- Credentials & Security: 4 tables
- Work Details: 6 tables
- Tagging: 3 tables (+ 2 junction)
- System: 2 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 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

---

## Open Questions

### MSP Mode Behaviors (To Define)
1. What should happen when `/msp` is called?
   - Prompt for client selection?
   - List recent clients?
   - Auto-detect from context?

2. What work items should be tracked?
   - Commands run?
   - Files modified?
   - Problems solved?
   - Credentials accessed?

3. Time tracking approach?
   - Auto-start timer on `/msp`?
   - Manual start/stop?
   - Track idle time?

4. Session end behavior?
   - Auto-save to database?
   - Generate summary?
   - Prompt for billable hours?

5. Credential handling?
   - Store in database encrypted?
   - Retrieve on demand?
   - Audit access?

### Development Mode Behaviors (To Define)
1. What should Development Mode track?
2. How does it differ from normal mode?
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

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

+- 37 session logs (Dec 2025 - Jan 2026)

+- All project directories and documentation

+- Infrastructure and client network patterns

+**Total Tables:** 22 core tables + 5 junction tables = **27 tables**

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

+Master table for all client organizations.

+    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

+    type VARCHAR(50) NOT NULL CHECK(type IN ('msp_client', 'internal', 'project')),

+    domain_name VARCHAR(255), -- AD domain or primary domain

+    m365_tenant_id UUID, -- Microsoft 365 tenant ID

+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

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

+Individual projects/engagements for clients.

+    client_id UUID NOT NULL REFERENCES clients(id) ON DELETE CASCADE,

+    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')),

+    INDEX idx_projects_client (client_id),

+    INDEX idx_projects_status (status),

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

+    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,

+    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,

+    technician VARCHAR(255), -- "Mike Swanson", etc.

+    session_log_file VARCHAR(500), -- path to .md file

+    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)

+Individual tasks/actions within sessions (granular tracking).

+    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'

+        'completed', 'in_progress', 'blocked', 'pending', 'deferred'

+    affected_systems TEXT, -- JSON array: ["jupiter", "172.16.3.20"]

+    technologies_used TEXT, -- JSON array: ["docker", "mariadb"]

+    item_order INTEGER, -- sequence within session

+    INDEX idx_work_items_session (session_id),

+    INDEX idx_work_items_category (category),

+    INDEX idx_work_items_status (status)

+**Categories distribution (from analysis):**

+Open items across all clients/projects.

+    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,

+    blocked_by TEXT, -- what's blocking this

+    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,

+    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)

+Physical/logical locations for clients.

+    name VARCHAR(255) NOT NULL, -- "Main Office", "SLC - Salt Lake City"

+    vpn_required BOOLEAN DEFAULT false,

+    gateway_ip VARCHAR(45), -- IPv4/IPv6

+Servers, network devices, NAS, workstations.

+    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'

+    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'

+    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

+Applications/services running on infrastructure.

+    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"

+    protocol VARCHAR(50), -- "https", "ssh", "smb"

+    status VARCHAR(50) DEFAULT 'running' CHECK(status IN (

+        'running', 'stopped', 'error', 'maintenance'

+    INDEX idx_services_infrastructure (infrastructure_id),

+    INDEX idx_services_name (service_name),

+    INDEX idx_services_type (service_type)

+Dependencies and relationships between services.

+    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'

+    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)

+- GuruRMM API (hosted_on) Jupiter container

+    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'

+    INDEX idx_networks_client (client_id),

+Network security rules (for documentation/audit trail).

+    protocol VARCHAR(20), -- "tcp", "udp", "icmp"

+    action VARCHAR(20) CHECK(action IN ('allow', 'deny', 'drop')),

+    INDEX idx_firewall_infra (infrastructure_id)

+    tenant_id UUID NOT NULL UNIQUE, -- Microsoft tenant ID

+    tenant_name VARCHAR(255), -- "dataforth.com"

+    default_domain VARCHAR(255), -- "dataforthcorp.onmicrosoft.com"

+    cipp_name VARCHAR(255), -- name in CIPP portal

+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

+    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

+    INDEX idx_m365_tenant_id (tenant_id)

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

+Encrypted credential storage (values encrypted at rest).

+    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,

+    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"

+    password_encrypted BYTEA, -- AES-256-GCM encrypted

+    client_id_oauth VARCHAR(255), -- for OAuth

+    integration_code VARCHAR(255), -- for services like Autotask

+    requires_vpn BOOLEAN DEFAULT false,

+    requires_2fa BOOLEAN DEFAULT false,

+    ssh_key_auth_enabled BOOLEAN DEFAULT false,

+    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)

+- All sensitive fields encrypted with AES-256-GCM

+- Encryption key stored separately (environment variable or vault)

+    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

+    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)

+    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'

+    severity VARCHAR(50) CHECK(severity IN ('critical', 'high', 'medium', 'low')),

+    findings TEXT, -- investigation results

+    status VARCHAR(50) DEFAULT 'investigating' CHECK(status IN (

+        'investigating', 'contained', 'resolved', 'monitoring'

+    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

+Access control for credentials (future team expansion).

+    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

+    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,

+    INDEX idx_cred_perm_credential (credential_id),

+Track files created/modified/deleted during sessions.

+    work_item_id UUID NOT NULL REFERENCES work_items(id) ON DELETE CASCADE,

+    change_type VARCHAR(50) CHECK(change_type IN (

+        'created', 'modified', 'deleted', 'renamed', 'backed_up'

+    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

+    INDEX idx_file_changes_work_item (work_item_id),

+    INDEX idx_file_changes_session (session_id)

+    host VARCHAR(255), -- where executed: "jupiter", "172.16.3.20"

+    shell_type VARCHAR(50), -- "bash", "powershell", "sql", "docker"

+    output_summary TEXT, -- first/last lines or error

+    execution_order INTEGER, -- sequence within work item

+    INDEX idx_commands_work_item (work_item_id),

+    INDEX idx_commands_session (session_id),

+Audit trail for infrastructure modifications.

+    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

+        'dns', 'firewall', 'routing', 'ssl', 'container',

+        'service_config', 'hardware', 'network', 'storage'

+    target_system VARCHAR(255) NOT NULL,

+    verification_performed BOOLEAN DEFAULT false,

+    INDEX idx_infra_changes_work_item (work_item_id),

+    INDEX idx_infra_changes_session (session_id),

+    INDEX idx_infra_changes_infrastructure (infrastructure_id)

+Issue tracking with root cause and resolution.

+    error_message TEXT, -- exact error code/message

+    investigation_steps TEXT, -- JSON array of diagnostic commands

+    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)

+    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

+    service_id UUID REFERENCES services(id) ON DELETE SET NULL,

+    deployment_type VARCHAR(50) CHECK(deployment_type IN (

+        'code', 'config', 'database', 'container', 'service_restart'

+    deployed_from VARCHAR(500), -- source path or repo

+    deployed_to VARCHAR(500), -- destination

+    rollback_available BOOLEAN DEFAULT false,

+    INDEX idx_deployments_work_item (work_item_id),

+    INDEX idx_deployments_infrastructure (infrastructure_id),

+    INDEX idx_deployments_service (service_id)

+Track database schema/data modifications.

+    database_name VARCHAR(255) NOT NULL,

+    change_type VARCHAR(50) CHECK(change_type IN (

+        'schema', 'data', 'index', 'optimization', 'cleanup', 'migration'

+    size_freed_bytes BIGINT, -- for cleanup operations

+    backup_taken BOOLEAN DEFAULT false,

+    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)

+Flexible tagging system for work items.

+    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

+    category VARCHAR(50) CHECK(category IN (

+        'technology', 'client', 'infrastructure',

+        'problem_type', 'action', 'service'

+    usage_count INTEGER DEFAULT 0, -- auto-increment on use

+    INDEX idx_tags_category (category),

+**Pre-populated tags:** 157+ tags identified from analysis

+- 58 technology tags (docker, postgresql, apache, etc.)

+- 24 infrastructure tags (jupiter, saturn, pfsense, etc.)

+- 30 problem type tags (connection-timeout, ssl-error, etc.)

+- 25 action tags (migration, upgrade, cleanup, etc.)

+Many-to-many relationship: work items ↔ tags.

+    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),

+Many-to-many relationship: sessions ↔ tags.

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

+Track all API requests for security and debugging.

+    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

+    request_body TEXT, -- sanitized (no credentials)

+    response_status INTEGER, -- 200, 401, 500

+    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)

+Track database schema versions (Alembic migrations).

+    version_id VARCHAR(100) PRIMARY KEY,

+    applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

+1. **work_item_tags** - Work items ↔ Tags

+3. **project_relationships** (optional) - Projects ↔ Projects (related/dependent)

+4. **project_session_logs** (optional) - Projects ↔ Sessions (many-to-many if sessions span multiple projects)

+**Estimated Row Counts (1 year of MSP work):**

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

+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)

+4. ⏳ Seed initial data (tags, MSP infrastructure)

+5. ⏳ Create database on Jupiter MariaDB

+6. ⏳ Build FastAPI models (SQLAlchemy + Pydantic)

+9. ⏳ Build MSP Mode slash command integration


Perfect! The database schema has been completely documented in the spec file. Now I need to update the change log and let the user know the analysis is complete.


Edit

## 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

## 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