2: Configuration
data/app.json describes the build: which resources are loaded, where release output goes, and which resources belong only in release or only in dev.
2.1: Minimal configuration
{
"resources": [
"security/creds",
"security/security",
"security/token",
"payload",
"session",
"tag",
"phlo.async",
"DOM/form"
],
"release": "%app/release/"
}
Resources refer to the Phlo runtime catalog. That is framework code, not a place for app-specific files. You write app code as .phlo in the app path; only generic runtime functionality belongs in the catalog, deliberately.
2.2: Resources
Commonly used resources:
| Resource | Purpose |
|---|---|
security/creds |
Credentials from env and ini files |
security/security |
Security headers |
security/token |
Token generation |
payload |
Read POST, PUT, PATCH and uploads |
session |
Session object |
cookies |
Cookie object |
DOM/form |
Async forms |
DOM/link |
Async link navigation (<a.async>) |
phlo.async |
Backendhelper phlo_async() voor CLI-calls via de daemonpool of een eenmalig subprocess |
visitors |
Heartbeat/visitor tracking |
useragent |
User-agent parsing |
DB/DB, DB/MySQL, DB/model |
Database and ORM |
Only use resources the app actually needs. The Phlo Control Center can show available resources and dependencies.
Do not confuse the two async layers. SPA navigation and async forms run through the frontend runtime generated in www/app.js, with resources such as DOM/link and DOM/form. The separate phlo.async resource provides the backend function phlo_async() for dispatching a PHP/CLI call outside the current request.
To configure a resource for one app without forking it, inject a value onto its node from app.phlo (prop %AI.model = 'claude-opus-4-8', static %model.DB => %MySQL). See Advanced, "Modifying resources without forking".
2.3: Dev exclude
In a local dev build you often want to leave out certain tracking and realtime resources:
{
"exclude": [
"visitors",
"useragent",
"wsCast"
]
}
This applies to the dev build. The release build does not use this exclude automatically; visitor tracking can therefore still be active there.
2.4: Release
The short form is enough:
{
"release": "%app/release/"
}
Phlo then writes release PHP to release/ and web assets to release/www/.
2.5: Paths
%app/ refers to the app path from phlo_app(...). Keep path configuration in www/app.php and release/www/app.php as much as possible, so data/app.json stays about build behavior.
2.6: Namespaces and bundles
Every <style> and <script> block transpiles into a per-namespace bundle. ns=docs lands in www/docs.css and www/docs.js, ns=app,docs in both bundles, and blocks without ns= in the default namespace. A page selects its bundle with view(..., ns: 'docs').
Three keys in data/app.json control this:
{
"defaultNS": "app,docs",
"phloNS": ["app", "docs"],
"iconNS": "app"
}
defaultNS(default"app"): the namespace(s), comma-separated, for assets without an explicitns=. Resource assets (frontend helpers such asonExist, the cookiewall styles) carry nons=, so when your pages use multiple namespaces, widendefaultNSso every bundle gets them.phloNS(default["app"]): the namespaces whose JS bundle embeds the phlo.js runtime. Every namespace whose pages load standalone needs the runtime, so list them all here.phloJS: trueinverts the list: the runtime then goes into every namespace NOT inphloNS.iconNS(default"app"): the namespace that receives the generated icon-sprite CSS when theiconsengine is used.resourceNS(default{}): a per-resource namespace override, keyed by resource path (as listed inresources), e.g.{"DOM/markdown": "app", "themes/cobalt": "app"}. It forces that resource's<style>/<script>into the given namespace, overriding the block's ownns=. Use it to pull a resource's assets into a bundle of your choice (for example a theme intoappinstead of its owntheme.*bundle) without editing the resource.
Two rules keep a multi-namespace app healthy:
- Never work around a missing runtime by passing
defer: '/app.js'toview(). On async navigation that re-injects the bundle into a page that already has one and crashes with duplicate declarations. ConfigurephloNSinstead. - Two runtimes must never meet in one page. Links that cross namespaces (an
apppage linking to adocspage) must be plain links, so the browser does a full page load. Only links within the same namespace getclass=asyncfor SPA navigation.
2.7: Generated output
Do not edit these files by hand:
php/
www/app.js
www/app.css
release/
Change the .phlo source (any dev request rebuilds it; build::run does the same from the CLI), check with build::lint, and then create a release with build::release.
2.8: Credentials
The security/creds resource resolves secrets (API keys, database logins, webhook tokens) and exposes them as %creds->.... It reads from two sources, so the same code runs on a laptop and in production without edits.
The INI file data/creds.ini is the simplest source. Keep it out of version control. A simple secret is a top-level key; a structured one is a section with subkeys:
OpenAI = sk-...
Claude = sk-ant-...
Grok = xai-...
[mysql]
host = 127.0.0.1
database = app
user = app
password = secret
You read them as %creds->OpenAI (a scalar) and %creds->mysql->host (nested).
Environment variables provide the same values without a file, which suits CI and containers. The prefix PHLO__ marks a credential, and __ separates nesting levels:
PHLO__OpenAI=sk-...
PHLO__mysql__host=127.0.0.1
A host-scoped form, PHLO_<HOST>__..., applies only on a matching host. <HOST> is the request host uppercased with every non-alphanumeric turned into _, so factuur.software becomes FACTUUR_SOFTWARE:
PHLO_FACTUUR_SOFTWARE__OpenAI=sk-...
Sources are merged in order, each overriding the previous: data/creds.ini first, then PHLO__ globals, then host-scoped PHLO_<HOST>__ on top. So a host-scoped variable wins over a global one, which wins over the ini file.
A resource declares what it needs with @ requires: creds:<name> (for example creds:OpenAI, creds:mysql). That line is informational: it documents the key, it does not create it. Values are stored as sensitive, so %creds masks them in debug output.
Last updated on 23 August 2026