CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

CrumbForest Scanner Overview

CrumbForest is a PHP-based hardware inventory barcode scanner system for data center asset management. It runs on native LAMP stack (no Docker), uses JWT authentication, and integrates with Netbox for asset synchronization. The project includes an optional AI chat feature via Ollama and Qdrant.

Design Philosophy: The "Naked" Approach

This project deliberately follows a bash β†’ php β†’ mysql β†’ backend β†’ vanilla β†’ simple β†’ export workflow:

  1. bash first: Setup and deployment via shell scripts (doktor-setup.sh), not orchestration tools
  2. php: Native PHP 8.5+ on LAMP stack, no frameworks, strict types only
  3. mysql: Direct MySQL/MariaDB with PDO, no ORM abstraction
  4. backend: API-first design, all endpoints in single router file
  5. vanilla: Zero frontend build process, pure HTML/CSS/JS
  6. simple: No containers, no webpack, no complexity layers
  7. export: Ultimate goal is Netbox synchronization for asset management

Why "Naked"?

This is the anti-Docker, anti-modern-framework version of CrumbForest:

  • Removed from Docker version: Containers, docker-compose, SQLite support, auto-migrations, complex tooling
  • Added: Direct LAMP deployment, manual database setup, simpler file structure
  • Philosophy: "pepperPHP" - back to basics, maximum transparency, minimal abstraction

Trade-offs embraced:
- Manual setup over auto-provisioning
- Single-file routers over framework magic
- Direct SQL over ORM convenience
- Shell scripts over container orchestration
- Zero build step over modern frontend tooling

Result: Copy files β†’ configure Apache β†’ import SQL β†’ done. No images to build, no node_modules, no hidden complexity. Perfect for traditional Linux servers, RZ environments, and developers who prefer to see every moving part.

Key Commands

Initial Setup

# 1. Install PHP dependencies
composer install

# 2. Create MySQL database and user
mysql -u root -p
CREATE DATABASE crumbforest CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'crumbforest_user'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON crumbforest.* TO 'crumbforest_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

# 3. Import database schema
mysql -u crumbforest_user -p crumbforest < crumbforest_sql/schema.sql

# 3b. (Optional) Apply migrations for network topology
mysql -u crumbforest_user -p crumbforest < migrations/002_network_topology.sql
mysql -u crumbforest_user -p crumbforest < migrations/002_test_data.sql  # Test data

# 4. Configure environment
cp .env .env.backup  # if exists
nano .env  # Update DB credentials, JWT_SECRET, paths

# 5. Set file permissions (Linux/production)
sudo chown -R www-data:www-data .
sudo chmod 755 web_root
sudo chmod 775 logs uploads
sudo chmod 600 .env

# 6. Start Apache
sudo systemctl start apache2  # Linux
# OR
sudo brew services start httpd  # macOS with Homebrew

Development

# Validate setup before deployment
php check-setup.php

# Test database connection
php -r "new PDO('mysql:host=localhost;dbname=crumbforest', 'crumbforest_user', 'password');"

# Regenerate Composer autoloader after adding classes
composer dump-autoload -o

# Check Apache configuration
apachectl -t  # macOS
apache2ctl -t  # Linux

# View logs
tail -f logs/crumbforest.log
tail -f logs/error.log

# Integrated development helper (interactive menu)
./doktor-crumbforest.sh

# Quick setup on macOS M4
./macos-setup.sh

Testing (Manual API Tests)

# Health check
curl http://localhost/api/health

# Login test
curl -X POST http://localhost/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"crumbforest2024"}'

# Test file uploads
curl -X POST http://localhost/api/scans/upload \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "image=@test.jpg" \
  -F "barcode_data=TEST123"

Testing with PHPUnit

Setup

# Install PHPUnit (already in composer.json)
composer install

# Create test database
mysql -u root -p
CREATE DATABASE crumbforest_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON crumbforest_test.* TO 'crumbforest_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Running Tests

# Run all tests
./vendor/bin/phpunit

# Run only unit tests (fast, no database)
./vendor/bin/phpunit --testsuite Unit

# Run only integration tests (requires test database)
./vendor/bin/phpunit --testsuite Integration

# Run specific test class
./vendor/bin/phpunit tests/Unit/ConfigTest.php

# Run with coverage report (requires Xdebug)
./vendor/bin/phpunit --coverage-html coverage/

# Run in verbose mode
./vendor/bin/phpunit --verbose

Test Structure

Unit Tests (tests/Unit/):
- ConfigTest.php: .env parsing, getValue(), configuration validation
- AuthTest.php: JWT token generation/validation (without database)
- Fast, isolated, no external dependencies

Integration Tests (tests/Integration/):
- ApiAuthTest.php: Complete login flow with real database
- Tests actual database operations, token validation, user authentication
- Requires crumbforest_test database

Test Configuration

phpunit.xml:
- Separate test suites (Unit vs Integration)
- Environment variables for test database
- Code coverage for web_root/includes/

.env.testing:
- Separate test database (crumbforest_test)
- Test JWT secret
- Disabled AI services
- Never use production database for tests!

Writing New Tests

Unit Test Pattern:

namespace CrumbForest\Tests\Unit;

use PHPUnit\Framework\TestCase;

class MyClassTest extends TestCase
{
    protected function setUp(): void
    {
        // Reset state before each test
    }

    public function testSomething(): void
    {
        // Arrange
        $input = 'test';

        // Act
        $result = myFunction($input);

        // Assert
        $this->assertEquals('expected', $result);
    }
}

Integration Test Pattern:

namespace CrumbForest\Tests\Integration;

use CrumbForest\Database\Database;
use PHPUnit\Framework\TestCase;

class MyApiTest extends TestCase
{
    private Database $db;

    protected function setUp(): void
    {
        $this->db = new Database();
        // Initialize test data
    }

    public function testApiEndpoint(): void
    {
        // Test with real database
    }
}

Best Practices

  1. Unit tests first: Fast, isolated, no external dependencies
  2. Integration tests for critical paths: Login, database operations, API endpoints
  3. Use test database: Never run tests against production DB
  4. Test both success and failure cases: Invalid input, missing data, expired tokens
  5. Keep tests simple: Following the "naked" philosophy - no complex mocking frameworks

CI Integration (Optional)

The project philosophy is "simple", but if you need CI:

# .github/workflows/tests.yml (example)
name: Tests
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.1'
      - run: composer install
      - run: ./vendor/bin/phpunit --testsuite Unit

Note: Integration tests require MySQL setup in CI environment.

Database Migrations

Manual Migration Process

Following the "naked" philosophy, this project uses manual migrations (no auto-migration tools like Doctrine or Phinx). Migrations are applied by running SQL files directly.

# Apply a migration manually
mysql -u crumbforest_user -p crumbforest < migrations/002_network_topology.sql

# Check which migrations exist
ls -la migrations/

# Available migrations:
# - 002_network_topology.sql - Adds network hierarchy and cable tracking
# - 002_test_data.sql - Test data for 10.0.1.0/24 network (11 devices, 10 connections)
# - 002_unit_tests.sql - Validates network topology schema and data integrity

# Run unit tests for a migration
mysql -u crumbforest_user -p crumbforest < migrations/002_unit_tests.sql

Migration Naming Convention:
- Format: XXX_description.sql (e.g., 002_network_topology.sql)
- Sequential numbering (001, 002, 003, etc.)
- Always include comments explaining what the migration does

Important Notes:
- Migrations are NOT automatically tracked - you must manually keep track of which ones have been applied
- Always test migrations on a development database first
- Consider adding a migrations_log table if you need automated tracking
- For production, document applied migrations in deployment notes

Migration 002: Network Topology

Adds comprehensive datacenter network infrastructure tracking:
- network_parent_id: Self-referencing hierarchy (gateway β†’ switches β†’ servers)
- network_role: Device role (gateway, core-switch, distribution-switch, access-switch, server, storage, etc.)
- management_ip: Admin/management IP address
- port_count: Number of network ports
- network_connections table: Physical cable tracking with port-to-port mapping, VLAN support, IP addressing

Example hierarchy:

Gateway (10.0.1.1)
└─ Core-Switch
    β”œβ”€ Distribution-Switch-01
    β”‚   └─ Access-Switch-Rack01
    β”‚       β”œβ”€ Server-Web-01 (10.0.1.10)
    β”‚       └─ Server-DB-01 (10.0.1.11)
    └─ Distribution-Switch-02
        └─ Access-Switch-Rack02
            └─ Server-App-01 (10.0.1.30)

Architecture

Tech Stack

  • Backend: PHP 8.1+ with strict typing (declare(strict_types=1))
  • Database: MySQL/MariaDB (no SQLite support)
  • Frontend: Vanilla JavaScript single-page app (no framework)
  • Authentication: JWT tokens via firebase/php-jwt
  • Scanner: Html5-QRCode library for mobile camera access
  • Integration: Netbox REST API client
  • Optional: Ollama (LLM) + Qdrant (vector DB) for AI features

Directory Structure

web_root/                  # Apache DocumentRoot
β”œβ”€β”€ index.php              # Main router (delegates to api/ or public/)
β”œβ”€β”€ api/
β”‚   └── index.php          # REST API router - ALL endpoints defined here
β”œβ”€β”€ includes/              # PHP classes (PSR-4 autoloaded via composer)
β”‚   β”œβ”€β”€ Config/
β”‚   β”‚   └── Config.php     # .env loader, singleton pattern
β”‚   β”œβ”€β”€ Database/
β”‚   β”‚   └── Database.php   # PDO wrapper with connection pooling
β”‚   β”œβ”€β”€ Auth/
β”‚   β”‚   └── Auth.php       # JWT token management
β”‚   β”œβ”€β”€ Services/
β”‚   β”‚   └── ScannerService.php  # Core business logic
β”‚   └── Integration/
β”‚       └── NetboxClient.php    # Netbox API client
└── public/                # Frontend assets
    β”œβ”€β”€ index.html         # Single-page application
    β”œβ”€β”€ js/app.js          # Frontend logic
    └── css/styles.css

crumbforest_sql/
└── schema.sql             # Database schema with seed data

migrations/                # Manual database migrations (apply in order)
β”œβ”€β”€ 002_network_topology.sql  # Network hierarchy & cable tracking
β”œβ”€β”€ 002_test_data.sql         # Test network (10.0.1.0/24)
└── 002_unit_tests.sql        # Migration validation tests

.env                       # Configuration (NEVER commit!)
composer.json              # PHP dependencies + PSR-4 autoloading
doktor-crumbforest.sh      # Integrated dev tools (Git, API, MySQL, Tests)
macos-setup.sh             # Quick setup for macOS M4

Core Patterns

Namespace Structure:
- All PHP classes use CrumbForest\ namespace
- PSR-4 autoloading maps CrumbForest\ to web_root/includes/
- Example: CrumbForest\Config\Config β†’ web_root/includes/Config/Config.php

Configuration Management:
- Singleton Config class loads .env from project root
- Accessed via Config::get() (all values) or Config::getValue('key', 'default')
- .env is parsed manually (not using parse_ini_file) to support inline comments
- Path resolution: .env is 3 levels up from web_root/includes/Config/Config.php

Database Layer:
- Database class provides PDO connection with error handling
- Always use prepared statements: $stmt->execute(['param' => $value])
- Connection is created on-demand, not kept in long-lived state
- Password hashing uses password_hash() with PASSWORD_BCRYPT

Authentication Flow:
1. POST /api/auth/login with username/password β†’ receives JWT token
2. All protected endpoints require Authorization: Bearer <token> header
3. Auth::isAuthenticated($requiredGroup) validates token and user group
4. User groups: 1-5 = admin access, 10+ = regular users

API Structure:
- Single router file: web_root/api/index.php handles ALL endpoints
- Helper functions: jsonResponse(), errorResponse(), requireAuth()
- CORS headers enabled for frontend
- Input parsed from JSON body: file_get_contents('php://input')

Frontend Architecture:
- Pure JavaScript (no build step required)
- Single-page app with manual routing
- Stores JWT token in localStorage
- Html5-QRCode library for camera access (HTTPS required)

Database Schema

Key Tables:

Core Tables:
- users: Authentication (username, password_hash, email, user_group)
- scans: Device inventory (serial_number, manufacturer, model, barcode_data, image_path)
- Supports physical hierarchy via parent_id self-reference (rack β†’ server β†’ disk)
- Supports network hierarchy via network_parent_id (gateway β†’ switches β†’ servers)
- Network fields: network_role, management_ip, port_count, ip_address
- Tracks Netbox sync status: netbox_synced, netbox_id
- Added by Migration 002: Network topology fields

Network Topology (Migration 002):
- network_connections: Physical cable tracking between devices
- Fields: from_device_id, from_port, to_device_id, to_port
- Cable info: cable_type, cable_label, cable_color, cable_length_m
- Network config: ip_address, subnet_mask, gateway, vlan_id
- Link properties: speed, duplex, mtu, connection_type, status
- Use cases: Port mapping, cable management, network troubleshooting

Audit & Monitoring:
- asset_history: Audit log (scan_id, event_type, old_value, new_value)
- sensor_data: Optional monitoring data (device_id, sensor_type, value, threshold)

Important Fields:
- user_group: Lower = more privileges (1-5 admin, 10+ regular)
- status: Device state (pending, completed, etc.)
- metadata: JSON field for flexible additional data
- network_role: Device role in network (gateway, core-switch, distribution-switch, access-switch, server, storage, pdu, ups)
- parent_id vs network_parent_id: Physical containment vs network upstream connection

Important Notes

Security:
- .env must have 600 permissions and blocked from web access
- Change default JWT_SECRET to random value: openssl rand -base64 32
- Never use root MySQL user in production
- HTTPS required for camera access in browsers
- All database queries use prepared statements (PDO)

Common Issues:
1. "Class not found": Run composer dump-autoload -o
2. ".env file not found": Ensure .env is in project root (same level as composer.json)
3. "Database connection failed": Check MySQL service status and credentials in .env
4. Permission denied on uploads/logs: chmod 775 and ensure Apache user ownership
5. CORS errors: Frontend must use same origin or API CORS headers properly set

Code Style:
- All PHP files use strict types: declare(strict_types=1);
- Emoji comments ("KrΓΌmelsprache"): πŸ¦‰ sections, πŸ“‹ functions, πŸ” security
- "Merksatz" (mnemonic) comments explain purpose in simple German
- Error handling: throw exceptions or return error arrays, never die/exit mid-function

Deployment Workflow:
1. Database must be initialized manually (no auto-migration)
2. Apply migrations in order: migrations/002_network_topology.sql etc.
3. Composer dependencies installed before first run
4. Apache mod_rewrite must be enabled
5. DocumentRoot points to web_root/ directory
6. .env configured with production credentials
7. Validate with unit tests: mysql ... < migrations/002_unit_tests.sql

External Integrations:
- Netbox: REST API client in Integration/NetboxClient.php
- Ollama: Optional LLM at http://localhost:11434 (configurable)
- Qdrant: Optional vector DB at http://localhost:6333 (configurable)
- Enable via .env: AI_ENABLED=true

File Uploads:
- Handled by POST /api/scans/upload endpoint
- Allowed types: jpg, jpeg, png, heic, heif
- Max size configurable via MAX_UPLOAD_SIZE in .env
- Files stored in UPLOAD_DIR (default: uploads/)

No Build Process:
- Frontend is vanilla JS/HTML/CSS (no transpilation)
- PHP runs natively (no containers)
- Only Composer for autoloading

Default Credentials (from schema.sql):
- Username: admin
- Password: crumbforest2024
- Should be changed immediately after deployment