Snapshot of main at 9f39a6e1e (2026-11-02)

This commit is contained in:
Ronald Huynen committed 2026-11-02 10:41:47 +01:00
commit 6ccf1792d7
2580 files changed
+1040202

No files matched your search

+493
View File
@@ -0,0 +1,493 @@
# Multiple Platforms, Servers and Instances: White-Label Guide
How to set up and maintain a branded platform instance (Timebank.cc, Uuro or any other) running this
application. One instance runs per server; every instance runs the same code from git and differs only in
its `.env`, its platform folder `platform/` (config, theme, logos, favicons, text and template overrides; outside
git) and its database content.
The platform folder is the recommended place for everything platform-specific. The older way, gitignored files in
`config/` and uploaded branding files in `storage/`, keeps working; the platform folder wins where both exist.
| Instance | Server | Platform files (2026-10-19) |
|---|---|---|
| Timebank.cc | production | the older way: `config/timebank_cc.php`, `config/themes.php`; its logos and favicons are the defaults in the code. Moving it into `platform/` is planned (`php artisan platform:init --from-current`). |
| Uuro | staging | its own branch `uuro` until its Laravel 11 upgrade; then everything in `platform/` (runbook: [RUNBOOK_LARAVEL_10_TO_11.md](RUNBOOK_LARAVEL_10_TO_11.md)). |
Last checked against the code: 2026-10-19 (branch `laravel-11`, cd982cdd).
---
## Contents
1. [Checklist for a new platform instance](#1-checklist-for-a-new-platform-instance)
2. [What makes an instance different](#2-what-makes-an-instance-different)
3. [The platform folder](#3-the-platform-folder)
4. [Platform configuration](#4-platform-configuration)
5. [Theme](#5-theme)
6. [Logos and favicons](#6-logos-and-favicons)
7. [Platform name in texts and translations](#7-platform-name-in-texts-and-translations)
8. [Platform-specific pages and switches](#8-platform-specific-pages-and-switches)
9. [Footer](#9-footer)
10. [Custom content pages](#10-custom-content-pages)
11. [Keeping an instance up to date](#11-keeping-an-instance-up-to-date)
12. [Troubleshooting](#12-troubleshooting)
13. [Quick reference](#13-quick-reference)
14. [Related documentation](#14-related-documentation)
---
## 1. Checklist for a new platform instance
The server itself (PHP, MySQL, Redis, Elasticsearch, Reverb, mail) is set up with
[SETUP_GUIDE.md](SETUP_GUIDE.md) and [PRODUCTION_DEPLOYMENT.md](PRODUCTION_DEPLOYMENT.md). Then, for the
platform:
1. **`.env`**: set at least
- `APP_NAME`, `APP_URL`
- `TIMEBANK_CONFIG=<platform>`: the platform's name, e.g. `myorg`. `timebank_config()` reads the platform
config under this name; see [section 4](#4-platform-configuration).
- `TIMEBANK_THEME=<theme>`: `timebank_cc`, `uuro`, `vegetable` or `yellow`; not needed when the platform folder
has a `theme.php` of its own
- `DEPLOY_ENVIRONMENT=server`, `DEPLOY_APP_DIR`, `DEPLOY_SERVER_TYPE`, `DEPLOY_WS_URL`
- `DB_DEPLOY_USERNAME` / `DB_DEPLOY_PASSWORD`: a MySQL user that may alter and restore the database
- `MAIL_*`, `MAIL_SUPPORT_ADDRESS`, `MAIL_BOUNCE_ADDRESS`
2. **Platform folder** ([section 3](#3-the-platform-folder)), in the application root:
```bash
php artisan platform:init # platform/ from platform.example/
cp config/timebank-default.php.example platform/config.php # the platform config, from the template
```
3. **Platform config** `platform/config.php`: names, currency and terms per language, mail addresses,
account limits, the switches in [section 8](#8-platform-specific-pages-and-switches), and the footer. The
template uses `Example Timebank` and `@example.org` as placeholders: replace all of them.
4. **Theme**: pick a built-in one with `TIMEBANK_THEME`, or make your own in `platform/theme.php`
([section 5](#5-theme)).
5. **Branding files** in `platform/public/`: `mail-logo.png`, `mail-logo-inverted.png`, the favicons and
`og-image.png` ([section 6](#6-logos-and-favicons)); a logo of your own in `platform/views/logos/<theme>.blade.php`.
Without them, emails get a generated text logo with the platform name, and the site uses the default favicons.
6. **First deploy**: `./deploy.sh -e server`. It creates the config files the code still needs from their
templates (`config/themes.php` and the two template configs; not `config/<platform>.php` when
`platform/config.php` exists), merges new keys into `platform/config.php` and links `platform/public` at
`/platform`.
7. **Privacy policy**: put your policy in `references/gdpr/<platform>/<YYYY-MM-DD>/privacy-policy-FULL-<locale>.md`
(the download link on the privacy page reads the newest dated folder). Without it the download returns 404.
8. **Content**: static pages (FAQ, principles, getting started and so on) are posts in the database; create
them in the admin area ([section 10](#10-custom-content-pages)).
9. **Background services**: queue workers with all queues, Reverb and the scheduler
([QUEUE_WORKERS_SETUP.md](QUEUE_WORKERS_SETUP.md), [WEBSOCKET_SETUP.md](WEBSOCKET_SETUP.md)), search
([ELASTICSEARCH_SETUP.md](ELASTICSEARCH_SETUP.md)), bounce handling ([BOUNCE_SETUP.md](BOUNCE_SETUP.md)).
10. **Check**: open the site with your theme, log in, switch profiles, send a test mail
([EMAIL-TESTING-GUIDE.md](EMAIL-TESTING-GUIDE.md)), and look for the old brand: no "Timebank.cc",
no `example.org` addresses, no city pages you do not want. `php artisan platform:check` reports which config is
used and whether `/platform` is linked.
11. **Off-site copy**: put `platform/` in a private git repository of its own (no secrets in it: those stay in
`.env`); the server's backups stay on the server.
---
## 2. What makes an instance different
| Layer | In the platform folder (recommended) | The older way (still works) | In git |
|---|---|---|---|
| Environment, passwords | `.env` | `.env` | no |
| Platform behaviour, names, mail, limits, footer, switches | `platform/config.php` | `config/<TIMEBANK_CONFIG>.php` | no; templates `config/*.php.example` are |
| Theme colours, fonts, logo references | `platform/theme.php` | an entry in `config/themes.php`, chosen with `TIMEBANK_THEME` | no; template `config/themes.php.example` is |
| Website logo (SVG) | `platform/views/logos/<theme>.blade.php` | `resources/views/logos/<theme>.blade.php` | the older way is |
| Email/PDF logos, favicons, preview image | `platform/public/` | `storage/app/public/app-images/`, `public/<favicon_dir>`, `public/<og_image>` | no |
| Texts and templates | `platform/lang/`, `platform/views/` (overrides) | none: shared by all instances | no |
| Static page content, posts, categories | database | database | no |
| Privacy policy | `references/gdpr/<platform>/` | the same | yes |
Everything else, the application code and the translations, is shared by all instances; a platform changes
texts and templates through overrides in its platform folder, never in the shared files.
**Config protection.** The platform folder and the active config files are gitignored, so a `git pull` never
overwrites them.
`deploy.sh` creates a missing one from its `.example` template, and `php artisan config:merge` copies keys
that are new in the template into your file without changing your values. Details:
[CONFIG_MANAGEMENT.md](CONFIG_MANAGEMENT.md).
---
## 3. The platform folder
Everything that makes an installation one platform can live in one folder, `platform/` in the project root
(`PLATFORM_PATH` in `.env` moves it). It is outside git (`.gitignore`), so deploys, upgrades and rollbacks never
touch it, and the same code from `main` runs on every server. Every part is optional: without the folder, or
without one of its parts, the installation runs on `config/` and the files in the code as described in the
sections below. Loaded by `App\Providers\PlatformServiceProvider`.
| Path | What | Behaviour |
|---|---|---|
| `config.php` | The platform config | Replaces `config/<TIMEBANK_CONFIG>.php`; `config:merge` adds new keys to it |
| `theme.php` | One theme, with its `id` | Becomes the active theme (instead of `TIMEBANK_THEME`) |
| `lang/<locale>.json`, `lang/<locale>/<group>.php`, `lang/vendor/<package>/...` | Changed texts, only the keys you change | Merged key by key over the code's texts; placeholders keep working |
| `views/...`, `views/mail/...`, `views/vendor/<package>/...` | Changed templates: pages, emails, the email layout, the logo, package views | Replace the whole template |
| `public/` | Favicons, `mail-logo.png`, `mail-logo-inverted.png` | Served at `/platform/` after `php artisan platform:link` (deploy.sh runs it); used before the theme's files |
The tracked skeleton `platform.example/` (with a README per path) is the starting point.
```bash
php artisan platform:init # create platform/ from platform.example/
php artisan platform:init --from-current # also copy config/<name>.php and the active theme into it
php artisan platform:override emails.layouts.html # copy a template to change it (also mail::html.header,
# wirechat::components.divider)
php artisan platform:link # serve platform/public at /platform
php artisan platform:check # read-only: which config is used, stale overrides
```
**Texts or templates?** 31 of the 34 email templates take their wording from translation strings, so a text
override changes almost every email. Override a template only for another structure or layout.
**Overrides go stale.** A translation key is the English source sentence: when the code rewords it, an override
of the old sentence is no longer used. A copied template misses later fixes to its original.
`php artisan platform:check` reports both: keys the code no longer has (keys that the platform's own templates
use count as its own texts) and templates whose original changed since `platform:override` copied it (the first
line holds the original's fingerprint). Run it after every update.
**Moving an existing installation into the folder.** `php artisan platform:init --from-current`, copy the brand
files it lists into `platform/public/`, `php artisan platform:link`, check the site. `config/<name>.php` stays as a
fallback until you remove it; `platform:check` warns while both exist.
**Good practice.**
- No secrets in the folder: passwords and keys stay in `.env`. Then the folder can be a private git repository of
its own, which gives history and an off-site copy (the server backups stay on the server). The storage backup
(`scripts/backup-storage.sh`) also archives it, in `backups/platform/`.
- The web server only reads the folder; normal file permissions are enough.
- After a change: `php artisan view:clear` for templates; restart the queue workers and Reverb after a config
change (they read the config when they start).
- Tests never read a developer's own platform folder (`PLATFORM_PATH` in `phpunit.xml` points elsewhere): they run
on `config/<TIMEBANK_CONFIG>.php`. A developer whose config moved into `platform/` therefore keeps that file too
(92 tests failed on the template alone, 2026-10-19: they depend on the developer's config values).
---
## 4. Platform configuration
**File**: `platform/config.php`; without it, `config/<name>.php`, where `<name>` is `TIMEBANK_CONFIG` in `.env`.
Either way the config is read under that name, so code uses `timebank_config('key')`. Any name works:
`timebank_cc` and `timebank-default` exist as templates, and a platform can use its own (`uuro`, `myorg`):
- Start `platform/config.php` from `config/timebank-default.php.example`. Without a platform folder, `deploy.sh`
creates `config/<name>.php` from that template when it is missing, and adds it to `.git/info/exclude` on that
server, so deploys do not report it as an uncommitted change.
- A development machine keeps `config/<name>.php` even with a platform folder: the tests read that file and never the
platform folder.
- `php artisan config:merge <name>` (and `--all`) adds new keys to the platform config (to `platform/config.php`
when it exists) from `config/<name>.php.example` if that exists, otherwise from
`config/timebank-default.php.example`. Lists (footer links, payment types, rules) are left as they are.
Do not copy the active `config/timebank-default.php` of another server: it may be outdated or hold that
server's settings. Start from the template.
**Main sections** (see the comments in the template for every key):
| Key | What |
|---|---|
| `platform_translations.<locale>.*` | `platform_name`, `platform_name_legal`, `platform_name_short`, `platform_slogan`, currency name, plural, symbol and position, thousand separator, `platform_user(s)`, `platform_principles`, for each language (en, nl, de, es, fr) |
| `mail.*` | `system_admin`, `content_admin`, `payments`, `chat_messenger`, `support` addresses and names; `platform_sign_off_signature` per language |
| `accounts.<type>` and `profiles.<type>` | Account limits (`limit_min`, `limit_max` in minutes) and the transaction types an account may receive, per profile type |
| `permissions.<type>` | Which transaction types each profile type may make |
| `account_info.<type>.*` | Which balances and totals are shown publicly (`*_public`), to the owner (`*_self`) and to privileged viewers (`*_privileged`, see `privileged_viewer_types`) |
| `local_pages`, `android_app` | Platform-specific pages, see [section 8](#8-platform-specific-pages-and-switches) |
| `footer` | Footer sections and links, see [section 9](#9-footer) |
| `seo` | `allow_indexing_guest`, `allow_indexing_auth` |
| `profile_inactive`, `profile_incomplete`, `delete_profile` | See [PROFILE_INACTIVE_CONFIG.md](PROFILE_INACTIVE_CONFIG.md), [PROFILE_INCOMPLETE_CONFIG.md](PROFILE_INCOMPLETE_CONFIG.md), [PROFILE_AUTO_DELETE.md](PROFILE_AUTO_DELETE.md) |
| `main_search_bar`, `search_optimization` | Search behaviour and boosts, see [SEARCH_REFERENCE.md](SEARCH_REFERENCE.md) |
| `cert_monitor`, `health_monitor` | Certificate expiry warnings and the health check, mostly set through `.env` |
**Reading values in code**:
```php
timebank_config('accounts.user.limit_max'); // the active platform's value
timebank_config('local_pages.the-hague', true); // with a default for a missing key
config('app.timebank_config'); // the platform config name itself
platform_name(); platform_currency_symbol(); // wording helpers, current locale
```
---
## 5. Theme
**File**: `platform/theme.php`, one theme with its own `id`, which becomes the active theme. Without it: the theme
named by `TIMEBANK_THEME` in `config/themes.php` (template `config/themes.php.example`).
Themes are applied at runtime through CSS variables, so **one asset build serves every theme**: switching
`TIMEBANK_THEME` needs no `npm run build`. Only `VITE_*` values in `.env` (such as the Reverb host) are
compiled into the build.
**A theme of your own** belongs in `platform/theme.php`: copy a whole theme block from `config/themes.php.example`
(every colour key is needed: a missing one leaves its CSS variable empty), give it an `id`, and change the name,
colours, typography and logos. `php artisan platform:init --from-current` writes the active theme there for you.
Its logo view may live in the platform folder too (`platform/views/logos/mybrand.blade.php`): nothing of the theme
needs to be in the code.
```php
<?php
return [
'id' => 'mybrand',
'name' => 'My Brand',
'description' => '...',
'logos' => [
'svg_inline' => 'logos.mybrand', // platform/views/logos/mybrand.blade.php
'email_logo' => null, // platform/public/mail-logo.png is used first
'email_logo_inverted' => null, // platform/public/mail-logo-inverted.png
'favicon_dir' => null, // platform/public/ is used first
'og_image' => null, // platform/public/og-image.png is used first
],
'colors' => [ 'brand' => '#...', 'logo' => '#...', 'primary' => [50 => '#...', /* ... */ 900 => '#...'], /* ... */ ],
'typography' => [ 'font_family_body' => '...', 'font_family_heading' => '...', /* ... */ ],
];
```
The older way still works: the same block under a new key in `config/themes.php`, selected with `TIMEBANK_THEME`.
A new theme that should be available to every installation belongs in `config/themes.php.example` plus a
logo view in `resources/views/logos/`, committed to git.
**In views and code**, always use theme classes and helpers, never fixed colours: `bg-theme-brand`,
`text-theme-primary`, `border-theme-border`, `theme_color('primary.500')`, `theme_font('font_family_body')`.
UI patterns: [STYLE_GUIDE.md](STYLE_GUIDE.md), [THEME_CLASS_CONVERSION_GUIDE.md](THEME_CLASS_CONVERSION_GUIDE.md).
Technical details: [THEME_IMPLEMENTATION.md](THEME_IMPLEMENTATION.md).
---
## 6. Logos and favicons
Each asset is looked up in this order: the platform folder, the theme setting, the fallback.
| Asset | Platform folder | Theme setting (the older way) | Fallback when missing |
|---|---|---|---|
| Website logo (inline SVG) | `platform/views/logos/<theme>.blade.php` | `resources/views/logos/<theme>.blade.php` (in git; see `resources/views/logos/README.md`) | none: required for a theme |
| Email and PDF logo | `platform/public/mail-logo.png` | `storage/app/public/<logos.email_logo>`, e.g. `app-images/uuro_mail_logo.png` | a generated PNG with the platform name in the theme's logo colour; the `timebank_cc` theme falls back to `app_mail_logo.png` |
| Email logo for dark headers (newsletter) | `platform/public/mail-logo-inverted.png` | `storage/app/public/<logos.email_logo_inverted>` | the generated logo in white; `timebank_cc`: `app_mail_logo_inverted.png` |
| Favicons: `favicon.ico`, `favicon-32x32.png`, `favicon-16x16.png`, `apple-touch-icon.png` | `platform/public/` | `public/<logos.favicon_dir>/` | the files in `public/` |
| Optional icons: `favicon.svg`, `favicon-96x96.png`, `site.webmanifest` | `platform/public/` | `public/<logos.favicon_dir>/` | the file in `public/`, else not linked |
| Preview image for shared links (`og:image`, about 1200×630) | `platform/public/og-image.png` | `public/<logos.og_image>` | no preview image; the other Open Graph tags (title, site name, URL, locale) are always there |
Files in `platform/public/` are served at `/platform/...` once `php artisan platform:link` has run (`deploy.sh`
runs it). The layouts include one partial for the title, the icons and the Open Graph tags:
`resources/views/layouts/partials/brand-head.blade.php`.
**Email logo**: a PNG of about 900×700 pixels with a transparent background works best (emails show it about
120 pixels wide). Put it in `platform/public/mail-logo.png` (or, the older way, upload it to
`storage/app/public/app-images/` and name it in the theme). Code reads it
through `theme_email_logo_url()` (emails) and `theme_email_logo_file()` (PDFs), with `true` for the inverted logo; the generated fallback is
`App\Services\GeneratedLogo`, cached in `storage/app/public/app-images/generated/`.
**Favicons**: put the files in `platform/public/` (or, the older way, in
`storage/app/public/app-images/favicons/<theme>/` with `'favicon_dir' => 'storage/app-images/favicons/<theme>'` in
the theme). Layouts read them through `theme_favicon('favicon.ico')` and, for the optional ones,
`theme_favicon_optional()`; the preview image through `theme_og_image()`.
The platform folder, uploaded logos (`*_mail_logo*.png`) and favicons are gitignored, so they do not appear as
changes in `git status` and survive every deploy.
---
## 7. Platform name in texts and translations
Translations are in `resources/lang/<locale>.json` (and `resources/lang/<locale>/*.php`), shared by all
platforms. Strings that mention the platform use placeholders, replaced from `platform_translations`:
| Placeholder | Replaced by |
|---|---|
| `@PLATFORM_NAME@`, `@PLATFORM_NAME_SHORT@`, `@PLATFORM_NAME_LEGAL@`, `@PLATFORM_SLOGAN@` | names |
| `@PLATFORM_USER@`, `@PLATFORM_USERS@`, `@PLATFORM_PRINCIPLES@` | terms |
| `@PLATFORM_CURRENCY_NAME@`, `@PLATFORM_CURRENCY_NAME_PLURAL@`, `@PLATFORM_CURRENCY_SYMBOL@` | currency |
In code, render such a string with `trans_with_platform('Why @PLATFORM_NAME_SHORT@?')`, not with `__()`
(which leaves the placeholder in place). Never write a brand name into a translation key or a view. The
wording helpers `platform_name()`, `platform_users()`, `platform_currency_symbol()` and so on return the
values directly. Translation workflow: `references/translations/`.
A platform that wants other wording overrides single texts in its platform folder (`platform/lang/`,
[section 3](#3-the-platform-folder)) instead of changing the shared files.
---
## 8. Platform-specific pages and switches
Some pages only make sense for one platform. They are switched in the platform config:
```php
'local_pages' => [ // static pages about Timebank.cc's local groups
'the-hague' => false,
'lekkernassuh' => false,
'amst-brus-lisb' => false,
],
'android_app' => [
'enabled' => false, // the Android app page and the app's update check (/app/version)
'apk_file' => 'app-{version}.apk', // in storage/app/public/apk/; {version} is the version name
],
```
A page that is off has no route (404), and footer links to it are left out automatically. The default
template has all of them off; the Timebank.cc template has them on. A key missing from an older config keeps
the old behaviour (on) until `config:merge` adds it.
For an Android app of your own, see [ANDROID_APP_UPDATE_FLOW.md](ANDROID_APP_UPDATE_FLOW.md);
`scripts/deploy-apk.sh` uploads an APK to another server with `APK_REMOTE_HOST=... APK_REMOTE_APP_DIR=...`.
---
## 9. Footer
The footer is built from `footer` in the platform config:
```php
'footer' => [
'sections' => [
[
'title' => 'Who we are', // translation key
'order' => 1, // left to right
'visible' => true,
'links' => [
['route' => 'static-faq', 'title' => 'FAQ', 'order' => 1, 'visible' => true],
['url' => 'mailto:info@example.org', 'title' => 'Email us', 'order' => 2, 'visible' => true],
['route' => 'static-messenger', 'title' => 'Chat messenger', 'order' => 3, 'visible' => true,
'auth_required' => true], // only for logged-in users
],
],
],
'tagline' => 'Your time is currency', // translation key
],
```
- `route` is a route name from `routes/web.php`; a route that does not exist on this platform (e.g. a
switched-off local page) is skipped instead of causing an error.
- `url` is for external or `mailto:` links.
- `title` values are translation keys: add them to `resources/lang/*.json` for every language.
---
## 10. Custom content pages
Static pages show posts from the database: each page view (e.g. `resources/views/static/faq.blade.php`)
embeds `@livewire('static-post', ['type' => 'SiteContents\Static\Faq', ...])`, and admins write the content
as posts in that category. Limits per page are in `posts.static.<page>` of the platform config.
**A new page** (a code change, so for every platform):
1. Category type in `database/seeders/ExtendCategoriesSeeder.php`, e.g. `SiteContents\Static\MyPage`, and
its names and slugs in all 5 languages in `database/seeders/ExtendCategoryTranslationsSeeder.php`.
2. A view `resources/views/static/my-page.blade.php`, modelled on an existing static page.
3. A route in `routes/web.php`, in the localized group:
`Route::view(LaravelLocalization::transRoute('routes.static.my-page'), 'static.my-page')->name('static-my-page');`
with the URL slug per language in `resources/lang/<locale>/routes.php`.
4. Optional `posts.static.my-page` limits in both config templates, and a footer link.
5. `deploy.sh` runs both seeders; locally: `php artisan db:seed --class=ExtendCategoriesSeeder` and
`--class=ExtendCategoryTranslationsSeeder`.
To change seeded content on every installation (rename a category type or slug), add a step to
`app/Console/Commands/DatabaseUpdate.php` (`php artisan database:update`, run by `deploy.sh`).
---
## 11. Keeping an instance up to date
- **Routine deploys**: `./deploy.sh -e server`. It pulls `main`, offers to merge new config keys, installs
packages, migrates, re-indexes search and builds assets. Answer `y` and then `yes` per file to the config
merge; it adds new keys and keeps your values (backups in `storage/config-backups/`).
- **Check for new settings at any time**: `php artisan config:merge --all --dry-run`. Lists (footer
sections and links, payment types, validation rules) are the platform's own choice: a list the platform config
has is never changed by a merge; a missing one is added whole.
- **Stale overrides**: `php artisan platform:check` after every update.
- **Major upgrades** (e.g. Laravel 11 to 12) go through `scripts/upgrade.sh` (backup, maintenance window with tests,
rollback), never through a routine deploy: `deploy.sh` stops a server deploy that would raise the Laravel major
version. The Laravel 10 to 11 upgrade is described in [UPGRADE_LARAVEL_10_TO_11.md](UPGRADE_LARAVEL_10_TO_11.md);
its runbook is Uuro's, the last installation on Laravel 10, which leaves its own branch with
`upgrade.sh --leave-branch`.
After a merge, review new keys in your platform config: new switches default to the template's value
(off in the default template), and names or addresses from the template are placeholders.
---
## 12. Troubleshooting
| Problem | Check |
|---|---|
| Theme not applied | `platform/theme.php` has an `id` and wins over `TIMEBANK_THEME`; without it, `TIMEBANK_THEME` matches a key in `config/themes.php`. The theme has all colour keys (compare with `themes.php.example`). |
| Wrong or default settings | `php artisan platform:check` shows which config is used (`platform/config.php` wins over `config/<name>.php`); without a platform folder, `TIMEBANK_CONFIG` matches the file name without `.php`. Run `php artisan config:merge <name> --dry-run` for missing keys. Restart the queue workers and Reverb after a config change. |
| A changed text or template does not show | `php artisan platform:check`: the code may have reworded the sentence (the override's key no longer exists) or changed the original template. After changing a template: `php artisan view:clear`. |
| Logo, favicon or preview image from `platform/public/` not used | `public/platform` is not linked: `php artisan platform:link`. |
| Brand name of another platform in a text | Look for a literal name in the view or translation key and switch it to a placeholder with `trans_with_platform()` ([section 7](#7-platform-name-in-texts-and-translations)). Texts in posts are content: edit them in the admin area. |
| Email shows a text logo instead of yours | No `platform/public/mail-logo.png` (or it is not linked), and the file named in `logos.email_logo` is missing in `storage/app/public/`. |
| Footer link missing | `visible` is true, the route exists on this platform, the section is visible, and `auth_required` matches. |
| Footer link causes an error | Should not happen any more (unknown routes are skipped); check the `url`/`route` key names. |
| City or Android pages appear on another platform | `local_pages` / `android_app` are missing from its config: run `config:merge`, then set them to `false`. |
| Privacy policy download gives 404 | No `references/gdpr/<TIMEBANK_CONFIG>/<date>/privacy-policy-FULL-<locale>.md`. |
| `deploy.sh` asks about uncommitted changes | Run `git status`: files changed on the server. The platform folder, uploaded logos, favicons and your platform config are excluded automatically; anything else was edited by hand. |
| `config:merge` reports `not merged` or `Merged config is invalid` | The file was left unchanged; see [CONFIG_MANAGEMENT.md](CONFIG_MANAGEMENT.md). |
---
## 13. Quick reference
```bash
# .env
TIMEBANK_CONFIG=<platform> # config/<platform>.php
TIMEBANK_THEME=<theme> # a key in config/themes.php (platform/theme.php wins)
PLATFORM_PATH=platform # the platform folder (default)
# Commands
./deploy.sh -e server # deploy (creates missing configs, merges new keys)
php artisan config:merge --all --dry-run # new keys waiting
php artisan config:merge <name> # merge into one config
php artisan config:merge --restore # restore a config from its backup
php artisan database:update # data updates of seeded content
php artisan platform:init [--from-current] # create the platform folder
php artisan platform:override <view> # copy a template into it
php artisan platform:link # serve platform/public at /platform
php artisan platform:check # stale overrides, config in use
```
```php
timebank_config('key.path', $default) // platform setting
config('app.timebank_config') // platform config name
platform_name() trans_with_platform('...@PLATFORM_NAME@...')
theme_color('brand') theme_font('font_family_body') theme_logo('svg_inline')
theme_email_logo_url() theme_email_logo_file() theme_favicon('favicon.ico') theme_og_image()
platform_path('lang') // inside the platform folder
```
---
## 14. Related documentation
**Setting up a server and instance**
- [SETUP_GUIDE.md](SETUP_GUIDE.md): complete Debian server setup (PHP, MySQL, Redis, Elasticsearch, web server)
- [PRODUCTION_DEPLOYMENT.md](PRODUCTION_DEPLOYMENT.md): production deployment
- [EXTERNAL_SERVICES_REQUIREMENTS.md](EXTERNAL_SERVICES_REQUIREMENTS.md): external services an instance needs
- [QUEUE_WORKERS_SETUP.md](QUEUE_WORKERS_SETUP.md): queue workers (all queues)
- [WEBSOCKET_SETUP.md](WEBSOCKET_SETUP.md), [WEBSOCKET_SEPARATE_DOMAIN.md](WEBSOCKET_SEPARATE_DOMAIN.md): Reverb
- [ELASTICSEARCH_SETUP.md](ELASTICSEARCH_SETUP.md): search
- [SSL_CERTIFICATE_RENEWAL.md](SSL_CERTIFICATE_RENEWAL.md): certificates
- [DEPLOYMENT_HTMLPURIFIER_FIX.md](DEPLOYMENT_HTMLPURIFIER_FIX.md): HTMLPurifier cache directory
**Configuration and updates**
- [CONFIG_MANAGEMENT.md](CONFIG_MANAGEMENT.md): config templates, `config:merge`, backups, platforms with their own config name
- [UPGRADE_LARAVEL_10_TO_11.md](UPGRADE_LARAVEL_10_TO_11.md): the Laravel 10 to 11 upgrade; [RUNBOOK_LARAVEL_10_TO_11.md](RUNBOOK_LARAVEL_10_TO_11.md): its checklist for the Uuro server
- `platform.example/README.md`: what goes where in a platform folder
- [PROFILE_INACTIVE_CONFIG.md](PROFILE_INACTIVE_CONFIG.md), [PROFILE_INCOMPLETE_CONFIG.md](PROFILE_INCOMPLETE_CONFIG.md), [PROFILE_AUTO_DELETE.md](PROFILE_AUTO_DELETE.md), [PROFILE_DELETION_GRACE_PERIOD.md](PROFILE_DELETION_GRACE_PERIOD.md): profile lifecycle settings
- [SEARCH_REFERENCE.md](SEARCH_REFERENCE.md), [SEARCH_REFERENCE_SIMPLE.md](SEARCH_REFERENCE_SIMPLE.md): search settings and behaviour
**Look and feel**
- [THEME_IMPLEMENTATION.md](THEME_IMPLEMENTATION.md): how themes work
- [STYLE_GUIDE.md](STYLE_GUIDE.md): UI patterns for every view
- [THEME_CLASS_CONVERSION_GUIDE.md](THEME_CLASS_CONVERSION_GUIDE.md): converting fixed colours to theme classes
- `resources/views/logos/README.md`: website (SVG) logos
**Mail**
- [EMAIL-TESTING-GUIDE.md](EMAIL-TESTING-GUIDE.md), [ARTISAN-EMAIL-COMMAND-SUMMARY.md](ARTISAN-EMAIL-COMMAND-SUMMARY.md): testing emails per theme
- [BOUNCE_SETUP.md](BOUNCE_SETUP.md), [MAIL_BOUNCE_SYSTEM.md](MAIL_BOUNCE_SYSTEM.md): bounce handling
**Platform-specific features**
- [ANDROID_APP_UPDATE_FLOW.md](ANDROID_APP_UPDATE_FLOW.md): Android app and its update check
- `references/gdpr/<platform>/`: privacy policy per platform
- `references/translations/`: translation workflow
**For developers and AI agents**: `AGENTS.md` in the repository root.