# 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=`: the platform's name, e.g. `myorg`. `timebank_config()` reads the platform config under this name; see [section 4](#4-platform-configuration). - `TIMEBANK_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/.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/.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///privacy-policy-FULL-.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/.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/.blade.php` | `resources/views/logos/.blade.php` | the older way is | | Email/PDF logos, favicons, preview image | `platform/public/` | `storage/app/public/app-images/`, `public/`, `public/` | 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//` | 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/.php`; `config:merge` adds new keys to it | | `theme.php` | One theme, with its `id` | Becomes the active theme (instead of `TIMEBANK_THEME`) | | `lang/.json`, `lang//.php`, `lang/vendor//...` | Changed texts, only the keys you change | Merged key by key over the code's texts; placeholders keep working | | `views/...`, `views/mail/...`, `views/vendor//...` | 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/.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/.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/.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/.php`, where `` 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/.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/.php` even with a platform folder: the tests read that file and never the platform folder. - `php artisan config:merge ` (and `--all`) adds new keys to the platform config (to `platform/config.php` when it exists) from `config/.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..*` | `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.` and `profiles.` | Account limits (`limit_min`, `limit_max` in minutes) and the transaction types an account may receive, per profile type | | `permissions.` | Which transaction types each profile type may make | | `account_info..*` | 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 '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/.blade.php` | `resources/views/logos/.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/`, 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/` | 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//` | the files in `public/` | | Optional icons: `favicon.svg`, `favicon-96x96.png`, `site.webmanifest` | `platform/public/` | `public//` | the file in `public/`, else not linked | | Preview image for shared links (`og:image`, about 1200×630) | `platform/public/og-image.png` | `public/` | 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//` with `'favicon_dir' => 'storage/app-images/favicons/'` 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/.json` (and `resources/lang//*.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.` 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//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/.php`); without a platform folder, `TIMEBANK_CONFIG` matches the file name without `.php`. Run `php artisan config:merge --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///privacy-policy-FULL-.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= # config/.php TIMEBANK_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 # 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 # 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//`: privacy policy per platform - `references/translations/`: translation workflow **For developers and AI agents**: `AGENTS.md` in the repository root.