Files
server-manager/AGENTS.md

5.2 KiB

ServerManager — AGENTS.md

Repo-specific guidance for OpenCode sessions. Verified against code.

Stack

  • PHP 8.3+, custom MVC (not Laravel), MySQL/MariaDB, phpseclib 3.0, phpdotenv
  • Autoload: PSR-4 ServerManager\src/; src/Helpers/functions.php auto-loaded
  • Android companion app at android/ (Jetpack Compose, Material3, Retrofit, Kotlinx Serialization)

Entry point

public/index.phpApp::boot()App::run()Router::dispatch()

Commands

  • composer install — install deps (no dev deps in composer.json)
  • php bin/generate-key.php — generate master key at /etc/servermanager/master.key
  • sudo bash install.sh — full Ubuntu 22.04+/24.04+ install (needs sudo)
  • mysql servermanager < database/migrations/001_initial_schema.sql — db setup
  • export ANDROID_HOME=/opt/android-sdk && cd android && ./gradlew assembleDebug — build APK → public/sysadmin.apk

Non-obvious conventions

  • Route params use :id syntax (/servers/:id), not {id}. Patterns: :id(\d+), :slug([a-zA-Z0-9\-]+)
  • Router supports method spoofing via _method POST field
  • Session name is SM_SESSION; sessions stored server-side in sessions table
  • View engine: custom PHP templates (extract() + include), not Twig/Blade
  • SSH credentials encrypted with AES-256-CBC; master key at /etc/servermanager/master.key
  • vendor/ is properly gitignored now (/vendor/ in .gitignore, no leading space)
  • Android build outputs ignored: android/.gradle/, android/build/, android/app/build/, android/local.properties
  • Bump versionCode and versionName in android/app/build.gradle.kts on every code change to the Android app

Backend gotchas

  • View::json() has : never return type — calls exit immediately. Do not put code after it expecting to run.
  • AuditService::log(string $action, string $entityType, ?int $entityId, ?array $details = null, ?array $metadata = null) — requires 3 positional args minimum. Calling with 2 args causes Error 500.
  • RoleMiddleware defaults to 'admin' required level. Hierarchy: super_admin (3) > admin (2) > operator (1).
  • Role helper functions in views: is_super_admin(), is_admin(), is_operator()
  • CSRF: middleware checks $_POST['_csrf_token'] then $_SERVER['HTTP_X_CSRF_TOKEN']. Token expiry 7200s default.
  • API routes have no middleware (auth is manual via $this->authenticateRequest() in controller). Web routes use AuthMiddleware / RoleMiddleware / CSRFMiddleware.
  • Admin group routes (/admin/*) already include AuthMiddleware + RoleMiddleware; individual routes add CSRFMiddleware.
  • Rate limiting on POST /login only (via RateLimitMiddleware)
  • Every credential access must be logged at warning level via AuditService
  • Notification read receipts tracked in notification_reads table: (notification_id, user_id) unique pair, with read_at timestamp

Permission validation on server create/edit

  • When creating or editing a server, required SSH permissions can be specified and are validated via SSH before saving.
  • PermissionValidator::validate(array $serverConfig, array $permissions) connects to the remote server and runs check commands for each permission.
  • Permission types: sudo (checks passwordless sudo), command (checks command -v), file_read (test -r), file_write (test -w), custom (arbitrary command, exit code 0 = pass).
  • ServerPermission model handles CRUD for the server_permissions table.
  • Both web (ServerController::store/update) and API (ApiController::serverAdd/serverUpdate) flows include permission validation.
  • On validation failure, the web flow preserves form input via $_SESSION['_form_input'] and redirects back with error details.

Database

  • Migrations run manually: mysql servermanager < database/migrations/NNN_name.sql
  • 13 migrations: 001_initial_schema, 002_server_access, 003_teams, 004_soft_deletes, 005_app_config_and_notifications, 006_agent_tokens, 007_server_permission_checks, 008_notification_reads, 009_remove_notification_obsolete_columns, 010_user_fcm_tokens, 011_cleanup_unused_tables, 012_server_thresholds, 013_user_notification_prefs
  • Users table has role ENUM: super_admin, admin, operator
  • api_token column on users for Bearer token auth
  • server_permissions table stores required SSH permissions per server (FK to servers.id CASCADE)

PR creation workflow

  • If the current branch exists on remote (git ls-remote --heads origin <branch>), create a new PR from it via API: POST /api/v1/repos/devlab/server-manager/pulls with head=<branch>, base=master.
  • If the current branch does not exist on remote, create a new one from master first: git checkout -b <name> master, push it, then create the PR.
  • The API token for git.devlab.lat is embedded in the remote URL (agent:<token>@git.devlab.lat). Use -u "agent:<token>" for basic auth. The token is accessible via git remote get-url origin.

Testing / CI

  • No tests, no CI, no linter, no formatter, no typechecker exist in this repo

Notable missing files

  • .env.example does NOT exist (referenced in composer.json post-install-cmd but missing from repo)
  • No Makefile, Dockerfile, or CI workflows