5.0 KiB
5.0 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.phpauto-loaded - Android companion app at
android/(Jetpack Compose, Material3, Retrofit, Kotlinx Serialization)
Entry point
public/index.php → App::boot() → App::run() → Router::dispatch()
Commands
composer install— install deps (no dev deps incomposer.json)php bin/generate-key.php— generate master key at/etc/servermanager/master.keysudo bash install.sh— full Ubuntu 22.04+/24.04+ install (needs sudo)mysql servermanager < database/migrations/001_initial_schema.sql— db setupexport ANDROID_HOME=/opt/android-sdk && cd android && ./gradlew assembleDebug— build APK →public/sysadmin.apk
Non-obvious conventions
- Route params use
:idsyntax (/servers/:id), not{id}. Patterns::id→(\d+),:slug→([a-zA-Z0-9\-]+) - Router supports method spoofing via
_methodPOST field - Session name is
SM_SESSION; sessions stored server-side insessionstable - 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
versionCodeandversionNameinandroid/app/build.gradle.ktson every code change to the Android app
Backend gotchas
View::json()has: neverreturn type — callsexitimmediately. 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.RoleMiddlewaredefaults 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 useAuthMiddleware/RoleMiddleware/CSRFMiddleware. - Admin group routes (
/admin/*) already includeAuthMiddleware+RoleMiddleware; individual routes addCSRFMiddleware. - Rate limiting on
POST /loginonly (viaRateLimitMiddleware) - Every credential access must be logged at
warninglevel viaAuditService - Notification read receipts tracked in
notification_readstable:(notification_id, user_id)unique pair, withread_attimestamp
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(checkscommand -v),file_read(test -r),file_write(test -w),custom(arbitrary command, exit code 0 = pass). ServerPermissionmodel handles CRUD for theserver_permissionstable.- 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 - 8 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 - Users table has
roleENUM:super_admin,admin,operator api_tokencolumn on users for Bearer token authserver_permissionstable stores required SSH permissions per server (FK toservers.idCASCADE)
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/pullswithhead=<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.latis embedded in the remote URL (agent:<token>@git.devlab.lat). Use-u "agent:<token>"for basic auth. The token is accessible viagit remote get-url origin.
Testing / CI
- No tests, no CI, no linter, no formatter, no typechecker exist in this repo
Notable missing files
.env.exampledoes NOT exist (referenced incomposer.jsonpost-install-cmd but missing from repo)- No Makefile, Dockerfile, or CI workflows