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"
}

Two rules keep a multi-namespace app healthy:

  1. Never work around a missing runtime by passing defer: '/app.js' to view(). On async navigation that re-injects the bundle into a page that already has one and crashes with duplicate declarations. Configure phloNS instead.
  2. Two runtimes must never meet in one page. Links that cross namespaces (an app page linking to a docs page) must be plain links, so the browser does a full page load. Only links within the same namespace get class=async for 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.

We use essential cookies to make this site work. With your permission we also use analytics to improve the site.