24 KiB
AGENTS.md — Fleetbase
Architecture
- Dual-stack monorepo: Laravel 10 (PHP) backend + Ember.js 5.4 frontend + 12 Git submodules
- Docker services:
application(FrankenPHP/Caddy :8000),httpd(:80→8000),console(Nginx :4200),socket(SocketCluster :38000),database(MySQL 8.0 :3306),cache(Redis),queue,scheduler - Console port 4200, API port 8000 — both must be free or mapped differently
- Packages: Each
packages/<name>/is a Git submodule; contains PHP (composer.json+server/) and/or Ember code (addon/,app/,extension.json) - Core packages:
core-api,ember-core,ember-ui,dev-engine— framework layers all extensions depend on
Prerequisites & Constraints
- PHP >= 8.0, <= 8.2.30 (strict upper bound — 8.3+ will break)
- Node.js >= 22
- pnpm is the JS package manager (not npm/yarn) —
pnpm@11.0.9 - Composer uses private registry at
https://registry.fleetbase.io— authentication may be required for private packages - Git submodules must be initialized:
git submodule update --init --recursive
Commands
Docker (most common)
docker compose up -d # Start all services
docker compose exec application bash # Shell into API container
docker compose exec application php artisan … # Run artisan commands
docker compose exec application bash -c "./deploy.sh" # Run deployment script
Backend (api/)
cd api && composer install # Install PHP deps (uses fleetbase registry)
cd api && php artisan … # Laravel artisan (run inside container or locally with DB access)
cd api && php artisan test # Run PHPUnit tests
cd api && ./vendor/bin/php-cs-fixer fix # Fix PHP style
cd api && vendor/bin/phpstan analyse # Static analysis (in packages with phpstan.neon.dist)
Frontend (console/)
cd console && pnpm install # Install JS deps
cd console && pnpm start # Ember dev server (requires API + socket running)
cd console && pnpm lint # ESLint + stylelint + template-lint
cd console && pnpm test # Lint + ember test (QUnit via Testem)
cd console && pnpm build # Production build → dist/
Single test runs
cd api && php artisan test --filter=MyTest # PHP: single test class
cd api && php artisan test --filter=MyTest::testFoo # PHP: single test method
cd console && pnpm test --filter='My test name' # Ember: filter by test name
cd console && pnpm test --server # Ember: watch mode
Extensions (Custom Packages)
Each extension is a Git submodule under packages/<name>/ with this structure:
extension.json # Metadata (name, version, engine, api)
composer.json # PHP dependencies (if has API)
package.json # JS dependencies (if has Ember engine)
server/ # PHP API code (PSR-4)
addon/ # Ember engine files
app/ # Ember app files
Scaffold a new extension
flb scaffold # CLI wizard
# Or manually: create packages/<name>/ with extension.json, composer.json, package.json
# Register it as submodule: git submodule add <repo-url> packages/<name>
Link local extensions for development
node scripts/package-linker.mjs # Symlink all packages/ into api/ and console/
Package registry flow
flb register # One-time: create registry account
flb verify -e <email> -c <code> # Verify email
flb generate-token -e <email> # Get auth token
flb set-auth <token> # Save token for installs
flb install fleetbase/<extension> # Install extension from registry
Production Deployment (VPS: 37.27.183.102)
SSH key: Config/fleet_key (chmod 600, OpenSSH RSA private key)
SSH command: ssh -i Config/fleet_key root@37.27.183.102
Key env vars for production (docker-compose.override.yml)
ENVIRONMENT=production,APP_DEBUG=falseAPP_KEY— generate withdocker compose exec application bash -c "php artisan key:generate --show"APP_URL,CONSOLE_HOST— set to actual domain/IPSESSION_DOMAIN— domain for session cookiesSOCKETCLUSTER_OPTIONS— restrict origins to production domainsMAIL_*— configure real mailer (not log driver)FILESYSTEM_DRIVER=s3— S3 for file storage (not local disk)GOOGLE_MAPS_API_KEY,IPINFO_API_KEY,TWILIO_*— for full feature setREGISTRY_HOST,REGISTRY_PREINSTALLED_EXTENSIONS— extension registry configOSRM_HOST— routing engine URL
Production checklist
- Copy repo to server, run
docker compose up -d(with correct override) - Ensure ports 80/443/8000/4200/38000 are open in firewall
- Set up SSL/reverse proxy (Caddyfile for API, Nginx for console)
- Use external/managed MySQL in production (not bundled container)
- S3 for file storage (not local disk)
- Restrict
SOCKETCLUSTER_OPTIONSorigins
Environment File Locations
| Env | Notes |
|---|---|
docker-compose.override.yml |
Primary — env vars injected into all Docker services |
api/.env |
Only needed for local PHP runs outside Docker |
api/.env.example |
Template reference |
console/environments/.env.development |
Ember dev env |
console/environments/.env.production |
Ember prod env |
Git Submodule Gotchas
- Always
git submodule update --init --recursiveafter clone - Submodules track specific commits — working on a package means committing inside
packages/<name>/first, then updating the parent repo's submodule pointer - After
git pullin parent, rungit submodule update --recursiveto sync submodules - Package linker script (
scripts/package-linker.mjs) symlinks submodules into api/console/ for local dev — run it after changing module structure
Linting (all layers)
| Layer | Tool | Command |
|---|---|---|
| PHP | php-cs-fixer | ./vendor/bin/php-cs-fixer fix (in api/ or package) |
| PHP | PHPStan | vendor/bin/phpstan analyse (in packages with neon config) |
| JS | ESLint + Prettier | pnpm lint (in console/) |
| CSS | Stylelint | pnpm lint:css |
| Templates | ember-template-lint | pnpm lint:hbs |
| i18n | fleetbase-intl-lint | pnpm lint:intl |
Conventions
- Ember Octane edition — use Glimmer components (
@glimmer/component),<template>tags,@tracked - Tailwind CSS 3.4 is the styling framework,
inter-uiis the typeface - PHP controllers return API resources via
\App\Http\Resources\namespace - Caddy replaces traditional Nginx/Apache for the API server (FrankenPHP)
- SocketCluster handles real-time WebSocket connections
- Prettier config: 4-space tabs, single quotes for JS, double quotes for HBS, 190 print width
Session: 2026-05-24 — Biibaye Branding Extension
Branch Strategy
- main: Clean upstream from
github.com:fleetbase/fleetbase. Never commit here — onlygit pull. - custom-production: Working branch with custom branding + WhatsApp alterations. Created off
mainat v0.7.41.
Private Git Repo
- URL:
ssh://git@git.1.warancloud.com:2222/Ali/biibaye.git - SSH Key:
~/.ssh/deploy_key(same key present on both local and VPS) - SSH Config entry (on VPS at
/root/.ssh/config):Host git.1.warancloud.com IdentityFile ~/.ssh/deploy_key IdentitiesOnly yes AddKeysToAgent yes port 2222 user git
New Extension: packages/biibaye-branding/
Git submodule pointing at private repo. Provides all custom branding:
packages/biibaye-branding/
├── extension.json # "Biibaye Branding" v1.0.0
├── package.json # @biibaye/branding, fleetbase-extension + ember-engine keywords
├── index.js # Ember addon entry
├── config/environment.js # Engine config
├── addon/
│ ├── engine.js # Minimal Ember Engine (required by Fleetbase build system)
│ └── extension.js # Branding logic — runs at Ember boot
└── public/
├── favicon/ # 22 Biibaye-branded icon files (apple-icon-*, android-icon-*, ms-icon-*)
└── images/ # logo SVGs, icon PNGs
extension.js does 5 things on boot:
document.title = "Biibaye - Delivery System"(in both setupExtension and onEngineLoaded)intl.addTranslations('en-us', { 'app.name': 'Biibaye' })— runtime translation injection- Injects CSS:
--primary: #FF4500, dark themebackground-color: #343538 - Replaces all
<link rel="icon">/<link rel="apple-touch-icon">with Biibaye files - Updates meta tags:
msapplication-TileColor,theme-color,msapplication-TileImage
Why translations/en-us.yaml was removed: ember-intl merges translations at build time with host app files taking HIGHEST priority. console/translations/en-us.yaml defines app.name: Fleetbase, which always wins over any addon's app.name. Other Fleetbase extensions avoid this by using namespaced keys (e.g., fleet-ops.*, storefront.*). Solution: in overrideAppName(), look up the intl service from appInstance and call intl.addTranslations('en-us', { 'app.name': 'Biibaye' }) at runtime.
Key implementation detail: setupExtension() calls universe.extensionManager.ensureEngineLoaded('@biibaye/branding') to force the engine to boot. Without this, the engine never loads (root-mounted engines have no route to trigger boot). onEngineLoaded re-sets translation and title after boot, defeating any race with ember-page-title re-render.
Deploy Script: /opt/fleetbase/scripts/deploy-biibaye.sh
Located on VPS. Handles the Docker build context issue (console build context is ./console/, not repo root). Steps:
git pull origin mainin extension submodule- Copies extension into
console/packages/biibaye-branding/(Docker build context) - Copies
public/favicon/*andpublic/images/*intoconsole/public/(so files serve from root/favicon/*URLs) - NEW — Patch
console/app/index.html: Replace Fleetbase defaults to prevent branding flash during reload:sed -i 's|<title>Fleetbase Console</title>|<title>Biibaye - Delivery System</title>|' console/app/index.html sed -i 's|content="#da532c"|content="#FF4500"|' console/app/index.html sed -i 's|content="#ffffff"|content="#FF4500"|' console/app/index.html sed -i 's|href="/favicon/apple-touch-icon.png"|href="/favicon/apple-icon-180x180.png"|' console/app/index.html sed -i 's|href="/favicon/android-chrome-192x192.png"|href="/favicon/android-icon-192x192.png"|' console/app/index.html sed -i 's|href="/favicon/android-chrome-256x256.png"|href="/favicon/android-icon-144x144.png"|' console/app/index.html sed -i 's|color="#5bbad5"|color="#FF4500"|' console/app/index.html - NEW — Patch
console/tailwind.config.js: Replace sky-500 blue (#3485e2) palette with Biibaye orange (#FF4500). This is the PRIMARY fix for the color theme — without this, Fleetbase shows blue buttons/accents because all UI uses Tailwindsky-*classes directly:sed -i "s/'#e6f0fb'/'#FFF0E6'/" console/tailwind.config.js sed -i "s/'#bad5f5'/'#FFD0B3'/" console/tailwind.config.js sed -i "s/'#8dbbef'/'#FFB380'/" console/tailwind.config.js sed -i "s/'#61a0e8'/'#FF7A33'/" console/tailwind.config.js sed -i "s/'#3485e2'/'#FF4500'/" console/tailwind.config.js sed -i "s/'#1c6cc7'/'#E03D00'/" console/tailwind.config.js sed -i "s/'#16539a'/'#B33100'/" console/tailwind.config.js sed -i "s/'#103b6d'/'#8A2600'/" console/tailwind.config.js sed -i "s/'#092341'/'#611A00'/" console/tailwind.config.js - Adjusts pnpm workspace paths for Docker (
../packages/→packages/) - Injects
COPY packages/ packages/beforepnpm installin Dockerfile (so pnpm resolve the link dependency) - Removes
--frozen-lockfileduring build (new dep not in lockfile) - Runs
docker compose build console --no-cache - Restores all original configs (Dockerfile, package.json, workspace, index.html, tailwind.config.js) — restore is critical to keep the clean checkout intact
- Restarts console, httpd, application containers
Why both tailwind.config.js patching AND extension.js CSS injection?
- tailwind.config.js patching (build-time): Changes primary color at the Tailwind compilation level. This prevents the blue flash because the compiled
@fleetbase/console.cssalready contains orange colors. However,ember-ui's pre-compiled CSS invendor.css(like.btn.btn-primary) uses#3485e2at@applycompile time and is NOT affected by the console's tailwind config. - extension.js CSS injection (runtime): Acts as a safety net, overriding any blue that leaks through from
vendor.csswith!importantrules. Covers the gap where ember-ui's pre-compiled CSS still references blue.
To update branding assets in the future:
# Locally: replace files in packages/biibaye-branding/public/ then:
cd packages/biibaye-branding
git add -A && git commit -m "Update assets" && git push origin main
# On VPS:
ssh root@37.27.183.102
/opt/fleetbase/scripts/deploy-biibaye.sh
VPS Changes Made (37.27.183.102)
| What | Where | Details |
|---|---|---|
custom-production branch |
/opt/fleetbase/ |
Created off main, committed submodule addition |
packages/biibaye-branding submodule |
/opt/fleetbase/.gitmodules |
Points at private repo |
APP_NAME: "Biibaye" |
docker-compose.override.yml |
Was "Fleetbase" |
MAIL_FROM_NAME: "Biibaye" |
docker-compose.override.yml |
Was "Fleetbase" |
EXTENSIONS: "@biibaye/branding" |
console/fleetbase.config.json |
Critical — without this extension.js never runs |
| SSH config | /root/.ssh/config |
Added git.1.warancloud.com entry |
| SSH key | /root/.ssh/deploy_key |
Copied for private repo access |
| Deploy script | /opt/fleetbase/scripts/deploy-biibaye.sh |
Rebuilds console with branding |
VPS State (Pre-existing)
| What | Value |
|---|---|
| Fleetbase version | v0.7.41 |
| Domains | api.biibaye.com, console.biibaye.com, socket.biibaye.com |
.env.development |
API: http://37.27.183.102:8000 |
.env.production |
API: https://api.biibaye.com |
fleetbase.config.json |
API/Socket prod URLs |
| Docker services | All running (scheduler unhealthy — pre-existing) |
Branding Spec
| Property | Value |
|---|---|
| App name | Biibaye |
| Window title | Biibaye - Delivery System |
| Primary color | #FF4500 |
| Dark theme background | #343538 |
| Favicon files | 22 Biibaye-branded icons (apple-icon-, android-icon-, ms-icon-*) |
| Logo files | SVG-02.svg, icon.svg, icon.png, fleetbase-logo-svg.svg |
Troubleshooting: app.name Translation
Problem: {{t "app.name"}} still shows "Fleetbase" after extension loaded.
Root cause: ember-intl v6.3.2 build-time merge gives host app (console/translations/en-us.yaml) priority over addon translations. Key app.name: Fleetbase at line 2 of console translations defeats any addon override.
Fix: Instead of relying on build-time translations/en-us.yaml, use runtime injection via intl.addTranslations():
// In setupExtension + onEngineLoaded:
const intl = appInstance.lookup('service:intl');
intl.addTranslations('en-us', { 'app.name': 'Biibaye' });
Verification: Check that appInstance.lookup('service:intl') returns the intl service and addTranslations method exists. ember-intl 6.3.2 has this API confirmed via addTranslations in test-support.js.
Troubleshooting: Primary Color Not Applied (CSS Variables Don't Work)
Problem: Extension injected :root { --primary: #FF4500; } but Fleetbase UI still shows blue (#3485e2).
Root cause: Fleetbase does NOT use CSS custom properties for theming. All colors are hardcoded via Tailwind's sky-* palette (e.g., bg-sky-500, text-sky-500). The --primary variable is never referenced by any Fleetbase component or CSS rule.
Fix (two layers):
-
Build-time — Patch
tailwind.config.jsto replace the sky palette with Biibaye orange before the Docker build. This changes allsky-*class definitions in the compiledconsole.css. -
Runtime — extension.js injects
!importantCSS overrides targeting Tailwind utility classes (.bg-sky-500,.text-sky-500, etc.) and component classes (.btn.btn-primary). This catches any blue that leaks through fromember-ui's pre-compiledvendor.css.
Session: 2026-05-25 — CSS Color Fix + Fleetbase UI Removal
Two critical fixes applied
1. Post-build CSS color patching (the approach that finally worked)
Problem: Building tailwind.config.js patches into Docker failed repeatedly (host-side sed, Dockerfile-internal Node.js patching, post-build sed — none worked). The built CSS always retained Fleetbase blue rgba(52, 133, 226, ...).
Solution: console/patch-css-colors.js — a Node.js script that runs AFTER pnpm build inside the Dockerfile. It walks the dist/ directory tree, finds all .css files, and replaces Fleetbase blue hex/RGB values with Biibaye orange directly in the compiled output.
RUN pnpm build --environment $ENVIRONMENT
RUN node patch-index-html.js && node patch-css-colors.js
Result: 4 CSS files patched (console.css, vendor.css, fleetops-engine/engine.css, registry-bridge-engine/engine.css). Zero blue references remain — 11 orange refs in console.css, 16 in vendor.css.
2. Extension loading fix
Problem: The @biibaye/branding extension never loaded because extensions.json (generated at build time by scanning node_modules) didn't include it. The deploy script's sed commands to modify pnpm workspace paths were no-ops because the source files never contained the biibaye entries to begin with.
Solution: Added "@biibaye/branding": "link:../packages/biibaye-branding" to console/package.json dependencies. Committed to custom-production branch. The deploy script converts the link path from link:../packages/ (host) to link:packages/ (Docker build context) via sed. This is the ONLY reliable sed in the deploy script — the workspace.yaml sed was removed.
Result: extensions.json now contains @biibaye/branding. The extension loads, all runtime features fire on boot.
Extension.js Enhancements
Added in this session:
-
CSS color overrides — replaced non-functional
--primary: #FF4500CSS variables with!importantoverrides for actual Tailwind classes (.bg-sky-500,.text-sky-500,.border-sky-500,.btn.btn-primary, etc.) and dark themebody[data-theme="dark"]background. -
Fleetbase UI removal:
- Menu links — CSS rules hide
.support-user-nav-item(Help & Support),.docs-user-nav-item(Documentation), anda[href*="discord.gg"](Discord). - Default widgets —
removeFleetbaseWidgets()accesses the widget registry and removesdashboard#fleetbase-bloganddashboard#fleetbase-github-cardfrom bothwidgetanddefault-widgetlists.
- Menu links — CSS rules hide
Patch Scripts (in console/ directory on VPS)
| Script | Purpose | When runs |
|---|---|---|
patch-index-html.js |
Replace Fleetbase title/favicon/meta in dist/index.html | Docker: after pnpm build |
patch-css-colors.js |
Replace Fleetbase blue → Biibaye orange in all compiled CSS | Docker: after pnpm build |
patch-index-html.py |
Same as .js version but in Python (used on host) | Deprecated — uses .js version now |
patch-tailwind.js |
Replace sky palette in tailwind.config.js | Deprecated — didn't work reliably in Docker |
Deploy Script (current version)
Located at /opt/fleetbase/scripts/deploy-biibaye.sh on VPS. Current steps:
git pull origin mainin extension submodule- Copy extension into
console/packages/biibaye-branding/ - Copy public assets (favicon, images) into
console/public/ - Run
python3 patch-index-html.pyon host (patches sourceapp/index.html) - Adjust Dockerfile: remove
--frozen-lockfile, injectCOPY packages/ packages/, injectRUN node patch-index-html.jsbefore build - Adjust package.json: convert
link:../packages/→link:packages/for Docker context docker compose build console --no-cache- Restore all modified files (index.html, package.json, Dockerfile)
- Restart containers
Fleetbase Source Changes (custom-production branch)
| File | Change |
|---|---|
console/package.json |
Added "@biibaye/branding": "link:../packages/biibaye-branding" in dependencies |
AGENTS.md |
All session documentation |
These changes are mirrored in the custom-pr branch of the biibaye extension repo under fleetbase-source/ for safekeeping.
Session: 2026-05-25 — Email Template Rebrand
Problem
OTP email showed "14411 is your fleetbase verification code" (lowercase fleetbase), Fleetbase logo in email header, and "2026 Fleetbase..." in footer. Other notification emails (password reset, user invite, etc.) had hardcoded "Fleetbase" in subject lines and body text.
Root Cause
The application container had APP_NAME=Fleetbase despite docker-compose.override.yml setting APP_NAME: "Biibaye". The container was created before the override was applied and restart doesn't pick up new env vars — only --force-recreate does.
Fix Applied
-
Environment fix: Recreated application container with
--force-recreateto pick upAPP_NAME=Biibayefrom override. This fixed allconfig('app.name')references:- OTP email subject:
"XXXX is your Biibaye verification code"✓ - SMS code:
"Your Biibaye verification code is XXXX"✓ - Email footer:
"© 2026 Biibaye"✓ - Logo alt text:
"Biibaye Logo"✓ - Password reset (forgot):
"Your password reset link for Biibaye"✓ - User credentials:
"Your login credentials for ... on Biibaye"✓
- OTP email subject:
-
Hardcoded Fleetbase →
config('app.name'): Modified PHP source files to replace hardcoded "Fleetbase" strings. Applied via Docker volume mounts (overlay patched files over vendor directory):File Change PasswordReset.php"Your password reset link for Fleetbase"→config('app.name')UserInvited.php"...on Fleetbase!"→"...on " . config('app.name') . "!"UserAcceptedCompanyInvite.php"...on Fleetbase!"+"Thank you for using Fleetbase!"→config('app.name')TestMail.phpSubject set via config('app.name')in constructortest.blade.php"test email from Fleetbase"→{{ config('app.name') }}fleetbase.phpLogo URL → https://console.biibaye.com/images/icon.pngmail.phpFrom address default → no-reply@biibaye.com, name fallback →BiibayeUtils.phpgetDefaultMailFromAddressdefault →null(uses CONSOLE_HOST)StorefrontNetworkInvite.php->from('hello@fleetbase.io',...)→config('mail.from.address') -
Volume mounts in docker-compose.override.yml:
application: volumes: - ./api/patches/core-api/src/Notifications/PasswordReset.php:/fleetbase/api/vendor/fleetbase/core-api/src/Notifications/PasswordReset.php # ... (8 mount points total)This overlays the patched files over the pre-built Docker image without requiring a custom image build.
Email branding summary
| Element | Before | After |
|---|---|---|
| OTP subject | "XXXX is your fleetbase verification code" | "XXXX is your Biibaye verification code" |
| Email header logo | Fleetbase S3 logo | Biibaye icon (from admin setting, fallback: console.biibaye.com) |
| Email footer | "© 2026 Fleetbase" | "© 2026 Biibaye" |
| From address | hello@fleetbase.io (default) | no-reply@biibaye.com (default) |
| From name | Fleetbase (default) | Biibaye (default) |
| Password reset subject | "Your password reset link for Fleetbase" | "Your password reset link for Biibaye" |
| User invite subject | "...invited to join Company on Fleetbase!" | "...invited to join Company on Biibaye!" |