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 |
phlo.async |
Async frontend requests |
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.
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 compiles 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.
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, run build::run, 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.