Development Guide
This guide covers setting up a local development environment for contributing to Databasement.
Requirements
- PHP 8.4+
- Composer
- Node.js 20+ & npm
- Docker & Docker Compose
Quick Start
1. Clone and Setup
git clone https://github.com/David-Crty/databasement.git
cd databasement
make setup
This will:
- Install Composer dependencies
- Run database migrations
- Install npm dependencies
- Build frontend assets
You should be ready to go!
Open http://localhost:2226 in your browser to view the app.
2. Start Development Environment
make start
This starts all Docker services:
- php — FrankenPHP server on http://localhost:2226
- queue — Queue worker for async backup/restore jobs
- mysql — MySQL 8.0 on port 3306
- postgres — PostgreSQL 16 on port 5432
Test database credentials: admin / admin / testdb
Available Commands
All PHP commands run through Docker. Use the Makefile targets or docker compose exec app <command>.
Testing
make test # Run all tests
make test-filter FILTER=ServerTest # Run specific tests
make test-coverage # Run with coverage report
Code Quality
make lint-fix # Auto-fix code style with Laravel Pint
make lint-check # Check code style without fixing
make phpstan # Run PHPStan static analysis
Database
make migrate # Run pending migrations
make migrate-fresh # Drop all tables and re-migrate
make migrate-fresh-seed # Fresh migration with seeders
make db-seed # Run database seeders
Assets
npm run build # Build production assets
npm run dev # Start Vite dev server (HMR)
make build # Alternative: build via Makefile
Docker Services
make start # Start all services
docker compose logs -f # View logs from all services
docker compose logs -f queue # View queue worker logs
docker compose restart queue # Restart queue worker
docker compose down # Stop all services
OAuth / SSO Testing
Databasement includes a Dex OIDC server for local OAuth testing. The Dex service is commented out by default in docker-compose.yml.
1. Add Hosts Entry
The Dex server uses a custom hostname that must resolve both from your browser and from within Docker containers:
echo "127.0.0.1 dex-local" | sudo tee -a /etc/hosts
2. Enable Dex Service
Uncomment the dex service in docker-compose.yml:
3. Start Dex
docker compose up dex -d
4. Configure Environment
Add to your .env.local:
OAUTH_OIDC_ENABLED=true
OAUTH_OIDC_CLIENT_ID=databasement
OAUTH_OIDC_CLIENT_SECRET=databasement-secret
OAUTH_OIDC_BASE_URL=http://dex-local:5556/dex
OAUTH_OIDC_LABEL=SSO
Test User
| Password | |
|---|---|
user@databasement.com | databasement |
Testing Flow
- Ensure hosts entry is added (step 1)
- Start Dex:
docker compose up dex -d - Visit the login page at
http://localhost:2226/login - Click "Continue with SSO"
- Enter test credentials:
user@databasement.com/databasement - You'll be redirected back and logged in
OAuth Behavior Notes
- New users: Created automatically with the role defined by
OAUTH_DEFAULT_ROLE - Existing users: When an existing user logs in via OAuth (matching email), their account is linked and their password is cleared
- OAuth-only users: Cannot use password login — they must use the OAuth button
- Settings access: OAuth-only users don't see Password or Two-Factor settings (managed by the OAuth provider)
Running an Agent Locally
A remote agent polls the app over HTTPS and runs backups on its own network (for servers the app can't reach directly). For local development you can run an agent straight from your working copy — no image rebuild — by bind-mounting the repo into the dev PHP image.
1. Create an agent and copy its token
Create one in the UI (Agents → Add Agent), or via tinker:
docker compose exec --user application app php artisan tinker --execute '
$org = App\Models\Organization::where("is_default", true)->first();
$agent = App\Models\Agent::create(["name" => "local-test-agent", "organization_id" => $org->id]);
echo $agent->createToken("agent")->plainTextToken.PHP_EOL;
'
2. Run the agent
Attach it to the compose network so it can reach the app, databases, and the S3 (rustfs) volume by service name. The -v "$(pwd)":/app mount runs your local code (dev only):
docker run -d --rm \
--network databasement_default \
-v "$(pwd)":/app \
-e DATABASEMENT_URL='http://app:2226' \
-e DATABASEMENT_AGENT_TOKEN='<paste-token>' \
--name databasement-agent \
davidcrty/databasement-php \
php artisan agent:run
docker logs -f databasement-agent # follow output
docker rm -f databasement-agent # stop the agent
When DATABASEMENT_URL is set the image runs in agent mode (it execs php artisan agent:run and needs zero database configuration). Setting it explicitly here lets us also override the startup command for the bind-mount workflow.
3. Back up through the agent
Assign the agent to a database server (set agent_id) and run a backup. Agent-backed servers cannot use a local volume — use the seeded RustFS (S3) volume. The agent then claims the job, dumps the database, and uploads it to the volume.
Use --network databasement_default so app:2226, postgres:5432, and rustfs:9000 resolve by name. Alternatively use --network host with DATABASEMENT_URL=http://localhost:2226, but then the server host and S3 endpoint must also be reachable from the host network.
Git Hooks
Pre-commit hooks (via Husky) automatically run:
make lint-fix— Auto-format code with Laravel Pintmake test— Run all Pest tests
Ensure tests pass before committing.
Architecture Overview
Tech Stack
| Layer | Technology |
|---|---|
| Backend | Laravel 12, PHP 8.4+ |
| Frontend | Livewire, Mary UI, daisyUI, Tailwind CSS 4 |
| Testing | Pest PHP |
| Database | SQLite (dev), supports MySQL/PostgreSQL/MariaDB |
| Auth | Laravel Fortify with 2FA support |
Key Models
- DatabaseServer — Database connection configurations
- Volume — Storage destinations (local, S3)
- Backup — Backup configurations (schedule, retention, volume)
- Snapshot — Individual backup snapshots with metadata
- BackupJob — Tracks backup/restore job execution and logs
Key Services
- BackupTask — Executes database dumps, compression, and storage
- RestoreTask — Downloads, decompresses, and restores snapshots
- DatabaseProvider — Creates database handlers, tests connections, lists databases
- SshTunnelService — Establishes SSH tunnels for database connections through bastion hosts
- ShellProcessor — Executes shell commands with logging
Livewire Components
DatabaseServer/*— CRUD operations for database serversVolume/*— CRUD operations for storage volumesBackupJob/Index— Job listing with logs modalSnapshot/Index— Snapshot listing and managementSettings/*— User settings (Profile, Password, TwoFactor)RestoreModal— 3-step wizard for snapshot restoration
Backup & Restore Workflow
Backup Process:
- Connect to database server
- Establish SSH tunnel if configured
- Execute database-specific dump (mariadb-dump/pg_dump/sqlpackage/mongodump/redis-cli/cp)
- Compress with gzip
- Upload to configured volume (local/S3)
- Record snapshot metadata
Restore Process:
- Select source snapshot
- Download and decompress
- Validate compatibility (database types must match)
- Drop and recreate target database
- Restore SQL dump
Cross-Server Restore: Restore production snapshots to staging/preprod as long as database types match.
Configuration
Environment Variables
The .env file is committed to the repository and contains default development configuration. To override these values, create a .env.local file (which is gitignored).
Key development configuration:
# Application
APP_URL=http://localhost:2226
# Database (for application data)
DB_CONNECTION=sqlite
# Queue
QUEUE_CONNECTION=database
# AWS S3 (optional, for S3 volume testing)
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
Testing Strategy
We use Pest PHP for testing. Key principles:
- Test business logic and behaviors — Not framework internals
- Mock external services — AWS SDK, S3 client, etc.
- Don't mock models — Use real database interactions
What to Test
- Authorization (who can access what)
- Business logic (backup works, restore works, cleanup deletes correct snapshots)
- Integration points (external services, commands)
What NOT to Test
- Form validation rules (Laravel handles this)
- Eloquent relationships and cascades
- Session flash messages
- Framework behavior
Submitting Changes
- Create a feature branch from
main - Write tests for new functionality
- Ensure all tests pass:
make test - Run code quality checks:
make lint-fix && make phpstan - Submit a pull request with a clear description
For significant changes, open an issue first to discuss the approach.