Naslag
CLI
Elk commando van ./nimbly: setup, builds, tests, onderhoud, scheduler en statistieken.
Nimbly heeft een CLI in core/cli/nimbly.php. Je roept die bij voorkeur aan via de launcher ./nimbly in de hoofdmap:
./nimbly init
./nimbly up
./nimbly deps
./nimbly build
./nimbly watch
./nimbly test:run
./nimbly user:create
./nimbly module:install <name>
init, deps, build en watch gebruiken intern npm. Commando's die PHP gebruiken, draaien op de PHP van de host als die er is, en vallen anders vanzelf terug op Docker. Er zijn ook expliciete varianten:
php core/cli/nimbly.php system:setup # require host PHP
./nimbly --docker system:setup # force Docker Compose
Gelijkwaardige directe aanroepen:
php core/cli/nimbly.php system:setup
php core/cli/nimbly.php user:create
php core/cli/nimbly.php module:install <name>
php core/cli/nimbly.php routes:sync
php core/cli/nimbly.php index:rebuild [resource]
php core/cli/nimbly.php data:cache:clear
php core/cli/nimbly.php user:email-index:rebuild
php core/cli/nimbly.php system:repair
php core/cli/nimbly.php help
Documentatie opzoeken
De documentatiecommando's lezen alleen, en alleen de lokale NIMBLY.md. Gebruik ze om tijdens het werk de kleinste bruikbare sectie van de naslag te laden:
./nimbly docs:list
./nimbly docs:section "Template Syntax"
./nimbly docs:section "Template Syntax > Route templates"
./nimbly docs:search "router_accept"
Hoofdletters maken niet uit. Een sectiepad kan een geneste kop aanwijzen; een kopnaam zonder pad moet uniek zijn. Ontbrekende en dubbelzinnige secties geven een fout.
Commando's
./nimbly help toont de commando's. ./nimbly <command> --help (of -h) toont wat één commando doet en welke invoer het neemt, zonder het te draaien.
system:setup
Richt het lokale systeem in. Veilig om opnieuw te draaien: bestaande bestanden en records worden nooit overschreven.
Wat het doet:
- Maakt
.envmetAPP_ENV, een gegenereerdePEPPER, enBASE_PATHalleen bij installaties in een submap - Maakt
.htaccessuit een sjabloon - Maakt
.user.iniuit een sjabloon voor de runtime-instellingen van PHP-FPM - Maakt de mappenstructuur van
ext/(data/,static/,lib/,modules/,tpl/,uri/, tijdelijke mappen) - Geeft een site zonder pagina's een eerste homepage (
ext/uri/index.tplenmain.tpl): een korte, bewerkbare pagina die laat zien waar pagina's, content en data staan, met één voorbeeldresource (ext/data/messages/.meta) en één formulier dat erin opslaat (ext/uri/message.json), dicht voor bezoekers. Die mag je aanpassen of weghalen; een site die al een route heeft, blijft ongemoeid - Maakt
.config/site, de resource.content, de.routes-records van core en de standaardrollen (admin,editor) - Maakt de resource
usersen een eerste admin-gebruiker
Vragen: Site name, Admin email, Admin password. Stappen die al klaar zijn, worden stil overgeslagen.
Zonder vragen (CI/CD): Zet omgevingsvariabelen om alle vragen over te slaan:
| Variabele | Beschrijving |
|---|---|
APP_ENV |
Naam van de omgeving (prod, stage, dev; standaard dev) |
BASE_PATH |
Optioneel basispad in de URL bij installaties in een submap, bijvoorbeeld /mysite/; weglaten bij installaties in de root |
PEPPER |
Pepper voor versleuteling; zet hem om een bestaande waarde te hergebruiken, laat hem weg om er automatisch een te maken |
EXT_REPO |
Git-remote-URL van de ext-repository (geschreven in ext/readme.md) |
SITE_NAME |
Naam van de site, geschreven in .config/site |
SITE_LANGUAGE |
Gecontroleerde hoofdtaal van twee kleine letters voor een nieuwe site (standaard en) |
ADMIN_EMAIL |
E-mailadres van de eerste admin-gebruiker |
ADMIN_PASSWORD |
Wachtwoord van de eerste admin-gebruiker (minimaal 8 tekens) |
SITE_NAME="My Site" ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=secret123 ./nimbly system:setup
Zonder terminal (bij het starten van een container, vanuit een scheduler) wacht de setup nooit op een antwoord. Een ontbrekende SITE_NAME wordt My Nimbly Site. Een site zonder gebruikers heeft ADMIN_EMAIL en ADMIN_PASSWORD nodig: ontbreekt er één, dan stopt de setup met één regel die hem noemt en exitcode 1, zodat een productiecontainer op een leeg volume pas start als beide zijn gezet.
user:create
Maakt een extra gebruikersaccount. Vraagt interactief om e-mailadres, rol en wachtwoord. De beschikbare rollen komen uit ext/data/roles/. De gebruiker krijgt altijd ook de rol user. Vereist dat de setup al heeft gedraaid.
module:install
Draait het script .install.inc van een module. Zoekt eerst in ext/modules/, daarna in core/modules/. Vereist dat de setup al heeft gedraaid.
./nimbly module:install event
routes:sync
Doorzoekt ext/uri/**/route.inc en maakt ontbrekende records in .routes voor dynamische routes. Gebruik dit nadat je een nieuwe routemap met (param)-segmenten hebt toegevoegd.
./nimbly routes:sync
index:rebuild
Bouwt de indexregels van een geïndexeerde resource opnieuw op. Gebruik dit nadat je index aan de .meta van een bestaande resource hebt toegevoegd, of nadat je records buiten de gewone API om hebt geïmporteerd.
php core/cli/nimbly.php index:rebuild # interactive: lists indexed resources, prompts for choice
php core/cli/nimbly.php index:rebuild articles # direct: reindex the 'articles' resource
Het commando loopt alle records van de resource langs en maakt ontbrekende indexbestanden. Je kunt het veilig vaker draaien; bestaande regels blijven onaangeroerd.
data:cache:clear
Gooit de gecachete queryresultaten van elke resource weg; de volgende keer lezen bouwt ze opnieuw op. Gebruik dit nadat je een recordbestand met de hand ter plekke hebt aangepast (zie Datacaching). Opslaan via de site, de API, Git of ext:sync vraagt geen legen.
php core/cli/nimbly.php data:cache:clear
user:email-index:rebuild
Voegt metadata voor het opzoeken op e-mailadres toe aan de resource users en bouwt de e-mailindex opnieuw op.
php core/cli/nimbly.php user:email-index:rebuild
php core/cli/nimbly.php user:email-index:rebuild --yes
Deze migratie hoort bij het model waarin de UUID van een gebruikersrecord vast is en het e-mailadres een geïndexeerd opzoekveld. Ze voegt email toe aan users/.meta.index, voegt email toe aan users/.meta.unique als er geen dubbele e-mailadressen zijn, en maakt indexregels voor alle bestaande gebruikers, ook oudere gebruikers waarvan de UUID oorspronkelijk md5(email) was.
Worden er dubbele e-mailadressen gevonden, dan meldt het commando die en voegt het unique=email pas toe als ze zijn opgelost. De e-mailindex wordt wel opnieuw opgebouwd, zodat inloggen waar mogelijk snel blijft.
test:run
Draait de E2E-tests. Het commando maakt tijdelijke testdata met test:setup, installeert zo nodig de Chromium-browser voor Playwright, draait Playwright met core/tests/playwright.config.js, en verwijdert daarna de tijdelijke data met test:teardown.
./nimbly test:run
./nimbly --docker test:run
Gebruik --docker in CI of wanneer PHP-databestanden via hetzelfde Docker-gebruikersmodel moeten worden geschreven als de webruntime. Core-tests roepen geen betaalde externe diensten aan. Vervang de provider door een mock en test het request, de verwerking van het antwoord, de validatie en het gedrag van de interface van Nimbly voorspelbaar. Of een provider bereikbaar is, is een operationele integratiecheck, geen onderdeel van de core-tests.
test:setup / test:teardown
Onderliggende commando's die ./nimbly test:run gebruikt. test:setup maakt de rol test, de gebruiker test@nimbly.dev, de resource test-records en startrecords. test:teardown verwijdert die records en de tijdelijke routeregels .test.
system:repair
Brengt gegenereerde bestanden en systeemrecords gelijk met de huidige core. Een core-update herschrijft ze nooit, dus een oudere site kan iets missen wat een nieuwe installatie van system:setup krijgt. Elke controle meldt eerst; zonder --yes verandert er niets.
| Controle | Gerepareerd als |
|---|---|
.htaccess in de root |
Die afwijkt van core/cli/setup/htaccess.tpl. De pepper en de rewrite-basis komen uit het bestaande bestand; het vorige bestand blijft bewaard als .htaccess.before-repair. |
| Mapbeveiligingen | Een .htaccess onder ext/ of core/ ontbreekt of nog de oude vorm <files *.*> heeft. Een beveiliging met eigen inhoud wordt gemeld en blijft staan. |
ext/.gitignore |
Die de thumbnailcache, de jobwachtrij, de status van de scheduler of de chats en runs van agents niet negeert. |
.config/.meta |
Die geen upsert heeft, zodat pagina-instellingen niet kunnen worden opgeslagen voor een pagina die er nog geen heeft. |
| Core-routes | Een core-route die na de setup is toegevoegd, niet is geregistreerd. |
| E-mailindex van gebruikers | users/.meta het veld email niet indexeert. unique wordt alleen toegevoegd als geen twee gebruikers hetzelfde adres hebben. |
./nimbly system:repair # report only
./nimbly system:repair --yes # apply
De genegeerde mappen veranderen op elke server vanzelf (de status van de scheduler elke minuut), dus als je er één volgt, krijgt ext:sync conflicten en kan ext/ op de server in een rebase blijven hangen. De negeerregel haalt een map die al wordt gevolgd, niet uit Git:
git -C ext rm -r --cached data/.state
schedule:run
Draait de geplande commando's die aan de beurt zijn voor één project. Maak op handmatige VPS-servers met meerdere Nimbly-projecten niet één cronregel per project. Gebruik in plaats daarvan de scheduler-orchestrator op serverniveau en laat die de schedule:run van elk geregistreerd project na elkaar aanroepen.
Projecten kunnen schema's definiëren in ext/cli/. De scheduler kiest bestanden in deze volgorde:
SCHEDULE_FILEuit.envof de omgeving van het proces, als expliciet bestandspad.ext/cli/schedule.<env>.inc, waarbij<env>uitSCHEDULE_ENV,APP_ENVofNIMBLY_ENVkomt.ext/cli/schedule.inc.core/cli/schedule.inc.
Namen van omgevingen worden gelijkgetrokken: production → prod, staging → stage, development en local → dev.
Gebruik schemabestanden per omgeving als staging of ontwikkeling achtergrondjobs moet draaien, maar geen taken die alleen voor productie zijn, zoals herinneringen aan leden.
Taken in het schema kunnen every_minutes gebruiken voor intervallen langer dan één minuut:
[
'id' => 'uptime-poll',
'command' => 'uptime:poll',
'every_minutes' => 2,
]
De waarde moet een positief geheel getal zijn. Een nieuwe taak is meteen aan de beurt; na het draaien is hij weer aan de beurt als de klok het volgende interval ingaat. Bestaande schema's met every, daily_at en de optionele timezone blijven ondersteund.
De status van de laatste runs van de scheduler staat in ext/data/.state/schedule. Bestaande installaties met de oudere status in ext/data/.config/schedule worden als terugval gelezen tot de scheduler het nieuwe statusbestand schrijft.
Verplicht onderhoud en het verlopen van sessies
Elke aanroep van schedule:run neemt de taken van core op voordat het schema van de applicatie wordt geladen. Schema's van de applicatie voegen taken toe; ze kunnen deze verplichte aanroepen niet weghalen of verplaatsen:
| Taak-id | Commando | Interval |
|---|---|---|
sessions-prune |
sessions:prune |
30 minuten |
jobs-run |
jobs:run 10 |
1 minuut |
jobs-prune |
jobs:prune --days=30 |
24 uur |
history-prune |
history:prune --days=90 |
24 uur |
agent-prune |
agent:prune --days=30 |
24 uur |
stats-rollup |
stats:rollup |
1 uur |
Bestaande regels in de applicatie met deze id's of commando's worden ontdubbeld. Vervang een implementatie, niet het schema ervan: definieer hetzelfde commando in ext/cli/commands.php, volgens de gewone afspraak voor het register van commando's. Core vertrouwt op de exitstatus van de implementatie en kijkt niet naar het werk daarbinnen. Het register op de host noemt projecten, geen keuzes van taken; oudere enabled-vlaggen houden geregistreerde projecten niet meer tegen. Haal deployments die niet meer draaien uit het register. Bekijk voordat je een ouder project zonder schema registreert de jobs in de wachtrij en het schema van de applicatie: als verwerking aangaat, kan wachtende e-mail worden verstuurd of ander werk uit de wachtrij worden uitgevoerd.
./nimbly schedule:status meldt of de verplichte taken zijn gelukt en stopt met een fout als een taak mislukte, nooit is gelukt, of meer dan twee intervallen te laat is. Een geslaagde run met een lege wachtrij is gezond. De band Needs attention in de admin en de host-audit lezen deze status elk zelf, dus ook zij merken een gestopte scheduler op. Controleer dit commando na elke deployment; alleen cron installeren bewijst niet dat het onderhoud werkt. De geïnstalleerde host-audit levert naast zijn collector een hulpbestand nimbly-maintenance.php dat van root is.
Gebruik voor VPS-installaties de bestaande orchestrator op de host en registreer alle actieve projecten. Containerimages roepen de scheduler van het project al elke minuut aan. Andere cloudplatforms moeten dezelfde CLI elke minuut aanroepen, met één scheduler die toegang heeft tot de blijvende ext/data en de runtime-instellingen van de applicatie. Bewaak schedule:status los van die runner. Er is geen extra daemon voor het opruimen van sessies en geen registratie per project nodig.
De functies voor sessies, inclusief het bekijken en opruimen van bestanden, staan in core/lib/session.php. De standaard is een doorlopende limiet op inactiviteit: anoniem 4 uur, ingelogde gebruikers 3 dagen, admins 7 dagen. Er is geen absolute limiet op de leeftijd van een login: regelmatige activiteit houdt een ingelogde sessie in leven. Het wisselen van de sessie-id verandert de identiteit niet en logt de gebruiker niet uit. Anoniem lezen en rechten controleren maakt geen sessies aan; formulieren en andere functies met status kunnen dat wel. Cookies van ingelogde gebruikers blijven bewaard na het herstarten van de browser; anonieme cookies blijven sessiecookies van de browser.
Elke cookie die core zet, heeft dezelfde opties (session_cookie_options()): het basispad van de site, HttpOnly, SameSite=Lax, en Secure op https. Dat geldt voor de sessiecookie, de formuliersleutel van een bezoeker zonder sessie (key) en een variabele die met [#set session ...#] wordt bewaard als er geen sessie is; die laatste twee blijven 30 dagen (persistent_cookie_options()). Scripts op de pagina kunnen ze niet lezen.
Projecten kunnen de duur in seconden instellen onder session in .config/site:
{
"session": {
"anonymous": 14400,
"authenticated": 259200,
"roles": { "admin": 604800, "editor": 259200 }
}
}
De langste passende uitzondering voor een toegewezen rol wint; anders geldt de standaard voor ingelogde gebruikers. Tijdelijk van rol wisselen verandert de vastgelegde toegewezen rollen niet. Inloggen, cookies en opruimen volgen dezelfde regels. Core zet de opruiming door PHP bij requests uit voor zijn eigen sessiemap.
./nimbly sessions:prune --dry-run meldt totalen zonder sessies te veranderen. Gewoon draaien leest de bestanden één voor één, slaat vergrendelde bestanden en symlinks over, en verwijdert alleen verlopen sessies. Kapotte bestanden, niet-ondersteunde geserialiseerde objecten of verwijzingen, en bestanden groter dan 1 MiB worden als fout gemeld in plaats van verwijderd. De lezer accepteert de coderingen php en php_serialize met waarden en arrays. De aparte actie Sign out everyone verwijdert bewust elke sessie. Een "onthoud mij"-optie met een langere duur is uitgesteld.
Requeststatistieken (stats:*)
Core legt elk request vast dat index.php bereikt, zonder cookies of JavaScript. Vastleggen staat aan voor APP_ENV=prod en stage; zet STATS_ENABLED=1 of 0 om dat te veranderen. Bestanden die .htaccess direct levert (ext/static, core/static, thumbnails onder /img/) en geblokkeerde .php-probes bereiken PHP nooit en worden niet vastgelegd.
Een shutdownfunctie voegt per request één JSON-regel toe aan ext/data/.tmp/stats/running-YYYY-MM-DD.log: UTC-tijd, methode, host, pad met query (ten opzichte van de basis-URL), status, contenttype, duur, IP-adres, user agent, verwijzer, de eerste tag van Accept-Language en een vlag voor ingelogd. Boven STATS_MAX_DAY_BYTES (standaard 200 MB) telt een dag verdere requests alleen nog als overloop.
stats:rollup (verplicht onderhoud, elk uur) archiveert afgesloten dagen onder ext/data/.stats/<env>/, die ext:sync commit:
| Pad | Inhoud |
|---|---|
raw/YYYY/YYYY-MM-DD.log.gz.enc |
de volledige dag, gecomprimeerd met gzip en versleuteld met AES-256-GCM |
raw/YYYY/YYYY-MM-DD.apache.log.gz.enc |
optionele aanvulling vanuit Apache, uit apache-YYYY-MM-DD.log |
months/YYYY-MM.json |
volledige tellingen per dag op status, route, soort bezoeker, apparaat, browser, taal, bot, verwijzer en pad, plus hours (requests, paginaweergaven, bezoekers, bots, scanners per UTC-uur) |
years/YYYY.json |
dezelfde tellingen opgeteld per maand, plus een jaartotaal |
Alle datums, bestandsnamen en dagovergangen zijn in UTC. Interfaces rekenen om naar de tijdzone van de kijker door hours opnieuw te groeperen; een bezoeker telt mee in het uur van zijn eerste paginaweergave van de UTC-dag. Zoom in van jaar naar maand naar dag naar de ruwe requests; weken en totalen over alle tijd worden uit deze bestanden afgeleid. Boven dagniveau wordt visitors visits, de som van de unieke bezoekers per dag. De JSON-bestanden bevatten alleen tellingen, nooit ranglijsten of IP-adressen; interfaces leiden toplijsten daaruit af. Het indelen (mens, redacteur, bot, scanner, verdacht, monitor, tool) gebeurt bij de rollup, dus stats:rollup --rebuild rekent elke maand opnieuw uit raw/ als de regels beter worden.
stats:import-apache leest regels uit het toegangslog van Apache (combined of vhost_combined) van stdin, zodat alleen het lezen van het log root nodig heeft:
zcat -f /var/log/apache2/site-access.log.* | sudo -u www-data ./nimbly stats:import-apache --mode=full --before=2026-09-25T02:02:00Z
zcat -f /var/log/apache2/site-access.log.1 | sudo -u www-data ./nimbly stats:import-apache
--mode=full vult elke regel aan vóór --before (dagen voordat het vastleggen begon). De standaard --mode=enrich voegt alleen toe wat PHP nooit bereikt: bestanden uit ext/static of core/static, thumbnails onder /img/ en geblokkeerde .php-probes. Regels worden geschreven naar apache-YYYY-MM-DD.log (UTC-datums) en bij de volgende rollup gearchiveerd; een watermerk in .tmp/stats/apache.watermark slaat regels over die al zijn geïmporteerd. Gebruik --base=/subdir/ voor installaties in een submap en --host= als SITE_URL niet is gezet. Regels van Apache hebben geen Accept-Language, contenttype of inlogstatus: contenttypen worden geraden uit het pad en redacteuren tellen als bezoekers.
STATS_KEY (64 hexadecimale tekens, gemaakt door system:setup) versleutelt het ruwe archief. Maak er een back-up van: zonder de sleutel is de ruwe geschiedenis niet te lezen. Zolang hij ontbreekt, mislukt de rollup en blijven de lopende logs bewaard. stats:raw YYYY-MM-DD [--apache] toont een ontsleutelde dag.
scheduler:*
Draait de schedulers van meerdere projecten vanuit één cron op serverniveau. De schemabestanden van een project blijven in het project zelf, in ext/cli/; het register op de server is de enige bron voor welke projecten worden aangestuurd.
De orchestrator gebruikt standaard deze bestanden, beheerd op de server:
| Pad | Doel |
|---|---|
/usr/local/bin/nimbly-scheduler-orchestrator |
Wrapper die cron draait |
/etc/nimbly/scheduler-projects.json |
Geregistreerde projecten en standaarden |
/etc/cron.d/nimbly-scheduler |
Cronregel van het systeem |
/var/log/nimbly-scheduler.log |
Log van de scheduler |
/etc/logrotate.d/nimbly-scheduler |
Wekelijkse rotatie van het log, acht weken bewaard |
Installeer het één keer per server vanuit een actuele checkout van Nimbly core:
sudo php core/cli/nimbly.php scheduler:install
sudo php core/cli/nimbly.php scheduler:project:add site-name /var/www/site-name
sudo php core/cli/nimbly.php scheduler:cron:install --user=www-data
Handige commando's voor beheer:
php core/cli/nimbly.php scheduler:project:list
sudo -u www-data php core/cli/nimbly.php scheduler:run --dry-run
sudo -u www-data /usr/local/bin/nimbly-scheduler-orchestrator
php core/cli/nimbly.php scheduler:cron:status
scheduler:run neemt kort een globale lock en start twee workers per geregistreerd project: één draait jobs:run 10 om de wachtrij vlot te verwerken, de andere draait de schedule:run van het project. Workers schrijven hun afronding en exitcode in het schedulerlog van de host. Een lange taak van een applicatie houdt dus de minuuttaken van een ander project, of de eigen meldingen in de wachtrij, niet op. Jobrunners gebruiken een lock per project, zodat de directe wachtrijworker en de verplichte taak jobs-run niet dezelfde job tegelijk oppakken. De projectscheduler houdt zijn eigen lock, zodat latere cronticks een lange run niet dubbel starten. Oudere waarden default_delay_after_seconds in het register worden genegeerd.
scheduler:run start de workers van elk geregistreerd project altijd meteen (het wacht nooit voordat het start), zodat een traag project het starten bij de volgende crontick niet ophoudt. Het aantal gelijktijdige workers wordt in plaats daarvan binnen elke scheduler:worker begrensd: voordat een worker de echte jobs:run/schedule:run van het project draait, wacht hij op een vrije plek in een gedeelde semafoor, en geeft die vrij als hij klaar is. Zo duwt een grote vloot niet alle workers tegelijk op een server met één CPU. De grens is standaard 5 gelijktijdige workers voor de hele host en in te stellen met NIMBLY_SCHEDULER_MAX_CONCURRENCY; de lockbestanden voor de plekken staan in NIMBLY_SCHEDULER_SEMAPHORE_DIR (standaard: de tijdelijke map van het systeem).