A resource is any database record born from versioned files: a layout, a page, a component, a text variable, an AI prompt, a scheduled task, a menu. You edit files; the pipeline compiles them into *Data.json; the updater writes them to the database.
The path of a resource
resources/<language>/<type>/<id>/<id>.html (+ .css, .precompiled.css, .md)
resources/<language>/<type>.json metadata (name, path, layout, version, checksum)
│ c2f resources:sync (atualizacao-dados-recursos.php)
▼
gestor/db/data/<Table>Data.json + gestor/db/data/schema-metadata.json
│ updater (atualizacoes-banco-de-dados.php), on deploy
▼
tables paginas, layouts, componentes, templates, variaveis, …
│ c2f css:rebuild
▼
css_precompiled regenerated from the database HTML, with a provenance signatureFor the system, the full pipeline is c2f manager:update-all. For a project, c2f project:update-all <id>. Both end with css:rebuild.
Where resources live
| Origin | Folder | Types |
|---|---|---|
| Core or project globals | gestor/resources/<language>/ with the files listed in resources.map.php |
layouts, pages, components, templates, variables |
| Modules | gestor/modulos/<id>/resources/<language>/, declared in the resources.<language> block of <id>.json |
the same plus ai_prompts, ai_prompts_targets, ai_modes, widgets (record only, no files) and forms |
| Scheduled tasks | cron key at the root of the module's <id>.json |
one per id, no language (cron.php library) |
| Declarative tables | tables_config.json (core), project_tables_config.json (project) or the module's tabela block, with sync_resources: true |
any table: menus, publisher_pages, pages_index… |
Languages come from resources.map.php (pt-br and en in the core). A resource that exists in only one language simply does not exist in the other.
Anatomy
gestor/resources/pt-br/pages.json
gestor/resources/pt-br/pages/404-pagina-nao-encontrada/404-pagina-nao-encontrada.html{
"name": "404 - Página Não Encontrada",
"id": "404-pagina-nao-encontrada",
"layout": "layout-pagina-sem-permissao",
"path": "404/",
"type": "page",
"without_permission": true,
"version": "1.4",
"checksum": { "html": "ee70…", "css": "", "combined": "ee70…" }
}Metadata uses English names that the compiler maps to the columns: name → nome, layout → layout_id, path → caminho, type → tipo (page → pagina, system → sistema), module → modulo, option → opcao, root → raiz, without_permission → sem_permissao. The Portuguese forms are accepted too. A page without path gets <id>/.
Version and checksum
-
Do not edit
versionorchecksumby hand. Step 2 ofresources:syncrecomputes each resource's checksum and, when it changed, incrementsversionand rewrites the source JSON. That is why aresources:syncusually leavespages.json,layouts.jsonand the<module>.jsonfiles changed in Git: this is expected and should be committed along with the change. -
The checksum is MD5 (
html,css,css_precompiledandcombined); prompts use the MD5 of the.md. It is not a security check, only a change detector. -
The version has the
X.Yformat and onlyYgoes up (1.27→1.28). A value outside this format (2.0.1,v2) is reset to1.0. -
*Data.jsonhas two versions:file_version(the source one) andversao, an integer that goes up when the checksum differs from the previous*Data.json.
Uniqueness and orphans
The compiler rejects duplicates and writes them to gestor/db/orphans/<Type>Data.json, outside the deploy:
| Type | Unique by |
|---|---|
| Layouts, components | language + module + id |
| Pages | language + module + id and language + path (no slashes, lowercase), even across modules |
| Templates | language + target + id |
| Variables | language + module + id + group. Without a group, only one |
| AI prompts, modes, targets, widgets | language + id |
| Cron tasks | id (also rejected without a callback or with an invalid frequency) |
The updater: what a deploy overwrites
The updater compares each *Data.json with its table and only processes the tables whose file changed since the last run. Each table's rules come from schema-metadata.json, generated by the compiler from tables_config.json, the modules' tabela block and project_tables_config.json:
-
strategy:natural_key(matches on thenatural_key_columns, such aslanguage, modulo, id) orpk. -
insert_only: inserts only, never updates (the case ofusuarios). It holds for both strategies: it is what stops the seed from rewriting an installation's administrator login, email and password. -
preserve_on_user_modified: fields protected when the record hasuser_modified=1, that is, it was edited in the admin panel. Inpaginas:nome, layout_id, caminho, framework_css, sem_permissao, html, css, css_compiled. -
deletarandforcar_atualizacao: lists of records to remove or to overwrite ignoring the protections (and settinguser_modifiedback to 0).
When a protected field differs, the system's new value is not lost: it goes to the mirror column (html_updated, css_updated; in variables, value_updated) and the record gets system_updated=1. Today no panel screen reads these columns: the system version is kept, but applying it is manual (or via forcar_atualizacao).
System seeds in a project. The tables that the core's tables_config.json seeds (modules, groups, operations, profiles, permissions, users, categories) are read, when compiling a project, from the project's seeds. With no seed in the project, the table is skipped (DYNAMIC_SKIP_PROJETO_SEM_SEMENTE in the log): the core's data reaches the database through the core sync, not through the project's db/data.
A failed write stops the build. If the compiler cannot write a *Data.json (a locked file, for instance), it retries and, if the failure persists, exits with an error. Carrying on would leave the database with the old content, because the resource's version and checksum would already have advanced in the source files.
Projects. On a project deploy (--project=<id>), the records touched get project=<id>. A later core update does not overwrite project records, except for unprotected fields of user-edited records, and never replaces their css_precompiled.
Updater options (php gestor/controladores/atualizacoes/atualizacoes-banco-de-dados.php): --dry-run, --log-diff, --debug, --force-all, --tables=paginas,variaveis, --skip-migrate, --backup, --reverse (exports the database to *Data.json), --orphans-mode=ignore|export|log, --hooks-only, --project=<id>, --env-dir=<domain>.
CSS: authored and derived
Each layout, page, component and template has four CSS columns:
| Column | Role | Written by |
|---|---|---|
css |
Authored: hand-written CSS (<id>.css) |
you |
css_precompiled |
Derived: Tailwind compiled for that HTML (<id>.precompiled.css) |
resources:sync (from the files) and css:rebuild (from the database) |
css_compiled |
Derived: the delta generated by the online editor | the visual editor |
css_source_hash |
Provenance signature: which HTML, CSS, layout and Tailwind version the derived CSS came from | whoever generates the derived CSS |
In the final HTML, each one goes into a tagged <style> (data-c2f-css-role="authored", data-tailwind-role="page-precompiled", data-c2f-css-role="compiled"), deduplicated by hash.
When a deploy changes the authored or derived CSS, the updater clears css_source_hash: the current CSS keeps being served, but the resource now counts as stale, and c2f css:rebuild recompiles it against the HTML in the database. This is what prevents the hybrid state "new HTML with old CSS". See c2f css:rebuild --help for --project, --tipo, --id, --todos and --dry-run.
Declarative tables (sync_resources)
Any table can become a resource with no new code. In the project's project_tables_config.json (or a module's tabela.config block):
{
"tabelas": {
"menus": {
"nome": "menus",
"id": "id",
"id_numerico": "id_menus",
"config": {
"scope": "global",
"strategy": "natural_key",
"natural_key_columns": ["language", "id"],
"sync_resources": true,
"metadata_file": "menus.json",
"field_types": { "html": "file:html", "css": "file:css", "fields_schema": "json" }
}
}
}
}-
scope:global(files ingestor/resources/) ormodule(files ingestor/modulos/<module>/resources/, with"modulo": "<id>"; themodulo/modulecolumn is filled in automatically). -
metadata_file: JSON with the list of records, in<base>/<language>/<resources_dir or table>/. Without it, records live inline inresources.<language>.<table>. -
field_types:jsonserializes the value;file:<ext>injects the content of<id>/<id>.<ext>. -
id: the column that identifies the record (it can be another one, such aspage_id). -
Records get
languageandstatus='A'when missing. -
The compiler generates
<Table>Data.json(menus→MenusData.json).
When project_tables_config.json exists, the compiler also writes project-schema-metadata.json at the project's Gestor root. It ships with the deploy so the server, which has no resources/, knows which tables belong to the project when answering _api/project/recover (the database pull).
Data hooks. A data-hooks.php in gestor/resources/, in gestor/db/ or in a module folder is included at the end of the compilation, to post-process the generated data.
Details and pitfalls
-
c2f resources:sync --forceshows up in the help, but it is not passed to the compiler: it has no effect. Likewise, the script's own options (such as--no-origin-update) only work by callingphp gestor/controladores/agents/arquitetura/atualizacao-dados-recursos.phpdirectly. -
After compiling,
resources:syncpublishes the assets topublic_html/dist/(assets:publish --opcional); withoutPUBLIC_PATH, it only warns. -
Static JavaScript, CSS and images (served by URL, outside
resources/) get an automatic cache token:resources:synccomputes the SHA-256 of each module's files and writesasset_versionto<module>.json, and does the same forgestor/assets/inassets/asset-versions.json. There is no need to bump the module version by hand; just commit those changed files. -
In development mode the runtime reads layouts, pages and components from disk, but not templates, variables or declarative tables.
See also
-
Request lifecycle: where the database HTML becomes a page.
-
gestor.php library:
gestor_componente(),gestor_layout(),gestor_pagina_recursos_incluir(). -
Skills
c2f-resources-system,c2f-project-pipeline-and-tasksandc2f-html-css-pages-and-components.