Author Topic: Help Docs
HannsGruber
Posts: 7
Registered: Jun 26
30sf5ms.gif

CoreBB API Contracts

The CoreBB API is a transport layer over the existing forum system, not asecond implementation of forum behavior.

API endpoints should prefer the same helpers and view-models used by desktoproutes. This keeps permissions, validation, SQL writes, notifications, logging,rate limits, and counters aligned across classic desktop pages and API clients.

Shared Helper Contract

These shared helpers now affect both desktop and API behavior:

  • Auth and session helpers in lib/api/auth.php, lib/security.php, andlib/auth_password_helpers.php
  • Registration helpers in lib/auth_view_model.php,lib/email_verification_helpers.php, and CreateUser()
  • Board, thread, profile, and post view-models
  • Posting helpers, especially corebb_post_process()
  • Private-message helpers, especially corebb_pm_send_from_post()
  • Moderation helpers in lib/moderation_helpers.php
  • Rate-limit helpers in lib/rate_limit_helpers.php

When changing any shared helper, test both the desktop route and the matchingAPI endpoint.

API Rules

  • API writes require an authenticated session unless the endpoint is public bydesign, such as registration.
  • API writes require CSRF validation with X-CoreBB-CSRF or an accepted bodytoken field.
  • API registration does not auto-login. Users must verify email before login.
  • API endpoints should not duplicate permission checks or SQL behavior when ashared CoreBB helper already exists.
  • If desktop behavior is embedded as a page side effect, prefer extracting ashared helper before adding API support.

Mobile Scope

The first mobile API scope includes:

  • Auth: CSRF, register, login, logout, and current viewer
  • Read-side forum data: index, board, thread, profile
  • Posting: preflight, reply, new topic, edit
  • Private messages: folders, message detail, send, mark read
  • Basic moderation: lock/unlock topic, remove/restore post, ban/unban user

The first mobile API scope intentionally excludes:

  • User private-message delete, because desktop PMs are indelible to users
  • PM moderation/removal, because it is advanced moderation
  • Admin tools and site-management actions
  • Archive import tooling
  • Maintenance/database/schema actions
  • Spam ratings generation

Compatibility Notes

The classic desktop UI remains the primary legacy-compatible surface. APIclients should treat API responses as versioned contracts, while desktop helpersremain shared system contracts.

When a behavior change is intentional, update this document and test theaffected desktop and API paths together.

HannsGruber
Posts: 7
Registered: Jun 26
30sf5ms.gif

Content Formatting Boundary

CoreBB public Twig views escape normal data by default. Any preformatted HTMLthat remains necessary on the public forum must pass through one narrow outputpipe:

{% include 'partials/formatted_content.twig' with {content: contentModel} only %}

That partial is the public display boundary for late-rendered content HTML.

Rulebook

  1. Public controllers and view models pass structured data, not HTML strings.
  2. Public Twig templates render normal text with regular Twig variables.
  3. Public Twig templates render formatted/user-authored/stored content only viapartials/formatted_content.twig.
  4. New public view-model fields should not be named *_html or html unlessthey are inside API payloads or admin-only models.
  5. Do not use |raw in public Twig templates except for the approved boundarycases listed below.
  6. If a dependency produces HTML, redesign the data flow so that HTML is createdas late as possible and consumed by the formatted-content partial.
  7. Admin pages, maintenance tools, and API serializers may have separate legacyHTML needs, but those exceptions do not apply to public Twig templates.

Approved Public Raw Outputs

  • views/layouts/public.twig: uses content|raw to place an already-renderedpublic page template into the public layout.
  • views/partials/public_head.twig: uses json_encode|raw for JavaScript data,not HTML.
  • views/partials/formatted_content.twig: renders the output ofcorebb_formatted_content_html().

No other public Twig template should use |raw.

Content Models

Use corebb_content_model($type, $body, $options) or one of its wrappers:

  • corebb_post_body_model() for posts, replies, blog entries, and signatures.
  • corebb_pm_body_model() for private-message bodies.
  • corebb_profile_bio_model() for profile bios.
  • corebb_profile_field_model() for profile fields that may need email/linkformatting.
  • corebb_stored_page_body_model() for trusted database-backed content such asthe Terms of Service setting.
  • corebb_search_highlight_model() for search result highlights.
  • corebb_user_title_model() for custom user titles.

The model tells the formatter what kind of content is being displayed. Twig onlydecides where it appears.

Stored HTML

stored_html is allowed only for trusted stored content, such as the Terms ofService body maintained by staff/admin tools. Do not use stored_html foruser-authored posts, private messages, signatures, profile fields, comments, orsearch input.

Adding A New Formatted Surface

When adding a public surface that needs markup:

  1. Add or reuse a content model helper in lib/content_format_helpers.php.
  2. Keep the controller/view model field structured, for example:['content' => corebb_post_body_model($body)].
  3. Render the field with partials/formatted_content.twig.
  4. Avoid putting rendered HTML in the model.
  5. Run the public raw-output scan:
rg -n --glob '!vendor/**' --glob '!cache/**' --glob '!views/pages/admin_*' "\|raw|_html\b|formatted_content_html" views lib functions.php

The expected public Twig hits are public.twig, public_head.twig, andformatted_content.twig.

Why This Exists

This keeps the Twig migration honest: backend code prepares content data andsecurity decisions, while Twig owns the display. The one intentional exceptionis late-rendered formatted content, because BBCode/stored-content conversionmust produce HTML at the final display step.

HannsGruber
Posts: 7
Registered: Jun 26
30sf5ms.gif

CoreBB Data Flow Guide

This guide explains how a browser request moves through CoreBB after the publictemplating and controller cleanup. It is meant for future maintainers who needto know where to add code without reintroducing root-file sprawl or mixing dataprocessing with display markup.

Request Boundaries

CoreBB is organized around a small set of boundaries:

  • .htaccess owns public URL mapping. Friendly routes such as /login/,/post/new/b12/, and /private-messages/ rewrite to internal controllers.
  • CookieEngine.php is the shared browser bootstrap. It starts the securityoutput filter, loads configuration and database access, starts the session,enforces normal form CSRF checks, and loads the signed-in user globals.
  • controllers/ contains public workflow entry points. Controllers should readrequest intent, check coarse access, call the right model or processor, thenrender or redirect.
  • lib/ contains the data and workflow layer. View models prepare templatearrays. Processors validate writes, enforce detailed permissions, call thedatabase helpers, and trigger side effects such as notifications.
  • views/ contains Twig layouts, pages, and partials. Twig owns display HTML.PHP should pass data, flags, URLs, and formatted-content models rather thanassembling page markup.
  • api/v1/ is the JSON front controller. API writes reuse the same lib/processors as browser forms so permissions and database behavior stay in oneplace.
  • admin.php remains the admin front controller. Admin view models live underlib/admin_*_view_model.php, and admin templates live underviews/pages/admin_*.twig.

Direct browser requests to controllers/ and views/ are blocked in.htaccess. Those folders are server-side source, not public assets.

Normal Public Page Flow

Most public pages follow this shape:

Browser URL
-> .htaccess rewrite
-> controllers/*.php or index.php
-> CookieEngine.php bootstrap
-> lib/*_view_model.php builds a model
-> corebb_render_public()
-> views/pages/*.twig
-> views/layouts/public.twig
-> HTML response

corebb_render_public() in lib/view.php captures the requested page template,builds shared chrome through corebb_public_layout_model(), and rendersviews/layouts/public.twig. Twig auto-escapes normal variables by default.Stored forum content that intentionally supports BBCode-style formatting shouldgo through the content formatting boundary documented indocs/content-formatting-boundary.md.

Normal Write Flow

Browser writes should be POST requests:

POST form
-> .htaccess rewrite
-> controller includes CookieEngine.php
-> CookieEngine.php validates CSRF for normal forms
-> controller checks login/method/action
-> lib processor validates input and permissions
-> lib/db.php prepared helpers read/write rows
-> processor returns a result model or redirect target
-> controller renders result page or redirects to the next public URL

Use the database helpers in lib/db.php for new code:

  • db_one() and db_all() for row reads.
  • db_value() and db_exists() for scalar/existence checks.
  • db_run() for writes.
  • db_begin(), db_commit(), and db_rollback() for multi-table writes.
  • db_insert_id() after inserts that need the generated id.

When a write updates more than one table, prefer a transaction. The postworkflow is the best current example.

Example: Forum Index, Board, And Topic Views

The forum landing page still starts at the root front door:

/
-> index.php
-> CookieEngine.php
-> lib/index_view_model.php: corebb_fetch_index_model()
-> views/pages/index.twig
-> views/partials/index_category_*.twig and index_forum_row.twig
-> views/layouts/public.twig

A board page follows the forum controller:

/some-board-slug/b12/p2/
-> .htaccess: controllers/forum.php?action=board&id=12&p=2
-> controllers/forum.php
-> lib/board_view_model.php: corebb_board_fetch_model()
-> views/pages/board.twig
-> views/partials/board_topic_row.twig

A topic page uses the same controller with a different action:

/some-board-slug/b12/345/p1/
-> .htaccess: controllers/forum.php?action=thread&id=345&brd=12&p=1
-> controllers/forum.php
-> lib/thread_view_model.php: corebb_thread_fetch_model()
-> views/pages/thread.twig
-> views/partials/thread_post.twig
-> views/partials/formatted_content.twig for post body output

Board and topic models are responsible for permission-aware data selection.Private-board and archive rules should be enforced before rows become visibleto Twig.

Example: User Creates A New Topic

The compose screen is a GET:

/post/new/b12/
-> .htaccess: controllers/post.php?boardid=12&act=new
-> controllers/post.php
-> login check
-> lib/post_view_model.php: corebb_post_form_model()
-> views/pages/post_form.twig

The submit is a POST:

/post/submit/
-> .htaccess: controllers/post.php
-> CookieEngine.php CSRF enforcement
-> controllers/post.php
-> lib/post_view_model.php: corebb_post_process()
-> corebb_post_process_new_topic()
-> db_begin()
-> INSERT topics
-> INSERT posts
-> optional poll creation
-> UPDATE forums/topics/users counters and activity
-> db_commit()
-> mention notifications
-> views/pages/post_result.twig

Replies and edits use the same controller and dispatcher:

  • corebb_post_process_reply() inserts a post, updates topic/board/useractivity, and sends reply/mention notifications.
  • corebb_post_process_edit() validates ownership or moderator access, enforcesedit timers for normal users, updates the post, and optionally updates stickystate for moderator edits.
  • corebb_post_process_blog() handles blog-entry submissions that enter throughthe same composer.

The API routes under /api/v1/post/... also call corebb_post_process().That keeps browser and API posting behavior aligned.

Example: Private Messages

PM folders are read-only page models:

/private-messages/
-> .htaccess: controllers/messages.php?action=folder&folder=unread
-> controllers/messages.php
-> login check
-> lib/pm_view_model.php: corebb_pm_folder_model()
-> lib/pm_helpers.php: corebb_pm_folder_result(), corebb_pm_counts()
-> views/pages/pm_folder.twig
-> views/partials/pm_message_row.twig

The compose page prepares display data only:

/private-messages/send/15/
-> controllers/messages.php?action=send&usr=15
-> lib/pm_send_view_model.php: corebb_pm_send_model()
-> views/pages/pm_send.twig

Sending a PM is a write:

POST /private-messages/send/
-> CookieEngine.php CSRF enforcement
-> controllers/messages.php
-> lib/pm_helpers.php: corebb_pm_send_from_post()
-> normalize recipients by username or user id
-> enforce recipient limits and rate limits
-> INSERT one privatemessages row per valid recipient
-> redirect to /private-messages/ with a status message

Viewing a PM is permission-scoped by sender/recipient:

/private-messages/message/99/unread/
-> controllers/messages.php?action=view&pm=99&method=unread
-> lib/pm_view_model.php: corebb_pm_view_model()
-> lib/pm_helpers.php: corebb_pm_get_for_view()
-> mark unread received message as read
-> views/pages/pm_view.twig
-> views/partials/formatted_content.twig for PM body output

PM reports are submitted from the PM view and flow throughcorebb_pm_report_private_message(), which validates that the reporting user cansee the message before creating a pm_reports row.

Example: Login, Registration, And Recovery

Authentication routes are consolidated under controllers/auth.php:

/login/
-> controllers/auth.php?action=login
-> lib/auth_view_model.php: corebb_login_model()
-> views/pages/login.twig

Login submission is intentionally redirect-only:

POST /login/submit/
-> CookieEngine.php CSRF enforcement
-> controllers/auth.php?action=login_submit
-> lib/auth_flow_helpers.php: corebb_auth_login_submit_redirect()
-> signed persistent-login cookie is issued by auth helpers
-> redirect to the next public URL

Registration, resend verification, password reset, and email verification usethe same controller with different actions. Their validation and email/tokenlogic belongs in the auth helper/view-model layer, not in Twig.

Example: User Control Panel

User CP is also action-based:

/user-cp/profile/
-> controllers/usercp.php?action=profile
-> login check
-> lib/usercp_settings_view_model.php
-> views/pages/edit_profile.twig

Each User CP POST is handled near the matching action incontrollers/usercp.php, but the data work lives in helpers such as:

  • corebb_usercp_save_profile()
  • corebb_avatar_handle_submit()
  • corebb_usercp_save_signature()
  • corebb_usercp_save_options()
  • corebb_user_appearance_save_self()

The controller should redirect after a successful settings write. That preventsbrowser refreshes from resubmitting the form.

Example: Blogs

Blog routes are grouped under controllers/blogs.php:

/blogs/
-> controllers/blogs.php?action=home
-> lib/blog_view_model.php: corebb_blog_home_model()
-> views/pages/blogs.twig

Entry and owner pages follow the same pattern:

/blogs/user/15/
-> corebb_blog_viewblog_model()
-> views/pages/blog_viewblog.twig

/blogs/entry/22/
-> corebb_blog_viewentry_model()
-> views/pages/blog_viewentry.twig

New blog entries use the post composer at /blogs/new/, which rewrites tocontrollers/post.php?act=blog. Blog edit/delete pages are still routed throughcontrollers/blogs.php and handled by lib/blog_view_model.php.

Example: Support And Moderation

Small public support pages are grouped in controllers/support.php:

/denied/              -> system message model
/banned/ -> unban request model
/board-rules-faq/ -> board rules and FAQ model
/contact-mods/ -> contact moderators model
/report-message/123/ -> post report model

Moderator actions are grouped in controllers/moderation.php. Keep moderatorpermission checks inside the moderation helper/model layer as close as possibleto the action being performed. A menu link being visible is not authorization.

Example: JSON API

The API front controller is api/v1/index.php:

/api/v1/post/reply/345
-> api/v1/index.php?path=post/reply/345
-> lib/api/bootstrap.php and guardrails
-> API rate limit and CSRF checks
-> corebb_api_viewer()
-> build a browser-form-compatible payload
-> lib/post_view_model.php: corebb_post_process()
-> JSON response

The API should reuse browser processors wherever possible. Do not duplicateposting, PM, moderation, or auth write logic in API-only code unless the behavioris truly API-specific.

Example: Admin Pages

Admin requests intentionally remain under admin.php:

/admin/?act=manage_boards
-> admin.php
-> CookieEngine.php
-> admin authentication and tool-access checks
-> lib/admin_boards_view_model.php
-> views/pages/admin_manage_boards.twig
-> views/layouts/admin.twig

The admin panel has a separate layout and permission model. Special tool accesslets a non-admin user enter specific admin tools, but each action still needsits own authorization check before it mutates data.

Where New Code Should Go

Use this checklist for new features:

  • Add or update the public URL in .htaccess.
  • Add a controller action under controllers/ when the feature is public.
  • Put data loading and write processing in lib/.
  • Put HTML in views/pages/*.twig and reusable fragments inviews/partials/*.twig.
  • Render public pages with corebb_render_public().
  • Redirect after successful POST writes.
  • Use corebb_public_url() or the Twig url() function for generated links.
  • Use db_*() helpers with bound parameters for database access.
  • Keep permission checks in the processor/model layer, not only in templates.
  • Send stored user content through the formatting boundary instead of handingraw HTML around inside models.

If a future change needs a new kind of route, copy the closest current flowfirst. The existing post, PM, and User CP paths are the best examples forwrite-heavy public features.

HannsGruber
Posts: 7
Registered: Jun 26
30sf5ms.gif

Non-Destructive Database Schema Deploys

CoreBB production schema deploys should be additive by default. The deploytool compares a target schema dump against either a live schema dump or theconfigured database, then generates only safe operations:

  • CREATE TABLE IF NOT EXISTS for tables missing from the destination.
  • ALTER TABLE ... ADD COLUMN for columns missing from existing tables.
  • ALTER TABLE ... ADD ... KEY for indexes missing from existing tables.

The tool never drops, renames, modifies, truncates, or deletes. Existing livetables, columns, indexes, archive structures, and definition drift arepreserved and reported as warnings.

Dry Run From Two Dumps

Use this before a production merge to review the exact schema delta:

php .\tools\db_schema_deploy.php `
--target-dump='staging-schema.sql' `
--live-dump='production-schema.sql'

If php is not available on PATH, replace it with the PHP executable pathfor the current server or workstation.

php .\tools\db_schema_deploy.php --target-dump=staging.sql --live-dump=live.sql

Web GUI

Administrators can use the browser-based deploy tool at:

/admin/?act=db_schema_deploy

The GUI accepts both workflows:

  • Paste or upload the staging target schema and production current schema.
  • Fetch either schema directly from a database using credentials entered on the page.

The page does not store database passwords. Password fields must be re-enteredfor each dry run or apply that fetches a schema from a database.

Dry-run mode may compare dumps only. Apply mode always reconnects to theproduction database using the production credentials on the page, inspects theactual live schema, rebuilds the plan, and only then applies non-destructiveoperations. Production apply also requires typing APPLY SCHEMA.

Production Apply

Run from the deployed production codebase after the release files and privateconfig are in place:

php tools/db_schema_deploy.php --target-dump=staging.sql --apply --confirm-non-destructive

Apply mode loads the normal CoreBB private config, inspects the currentdatabase with SHOW CREATE TABLE, rechecks each operation before running it,and skips anything already present. The confirmation flag is required so a dryrun cannot accidentally become a deploy.

Archive Safety

Archive and legacy identifiers are treated as protected schema. The tool marksoperations involving names such as archive, legacy, secure_archive,is_archive_user, vn, or vault as archive-sensitive. If the live databasecontains archive/legacy tables or columns that are not present in the targetdump, they are preserved and reported rather than removed.

That means a production archive can safely contain extra one-time importstructures or historical columns without the deploy tool trying to make livelook smaller.

Initial Release 1.0.0 Schema Delta

The final pre-release comparison that fed the initial public release producedtwo additive operations and no warnings:

  • Create admin_tool_permissions.
  • Create corebb_rate_limits.

No shared tables have column or index differences in the supplied dumps.

HannsGruber
Posts: 7
Registered: Jun 26
30sf5ms.gif

CoreBB Installation

CoreBB includes a web installer for fresh public installs. It is intended forempty databases only.

Requirements

  • PHP 8.1 or newer.
  • PDO MySQL extension.
  • MySQL or MariaDB with InnoDB and utf8mb4 support.
  • Writable private config location, preferably a corebb_private directory nextto the hosting public_html directory, outside web access.

Web Installer

Upload the release files, point the vhost at the CoreBB public directory, thenopen:

/install/

The installer:

  • Connects to the requested MySQL database.
  • Creates the database if it does not exist and the database user has permission.
  • Refuses to install into a non-empty database.
  • Creates the current CoreBB table structure from lib/install_schema.sql.
  • Seeds default settings, the default style rows, one public category, and onepublic board.
  • Creates the first level-5 administrator.
  • Writes private config to an instance-specific path such as../corebb_private/example_com_forum/config.local.php. On shared hostingpaths that include public_html, CoreBB prefers the siblingcorebb_private directory above public_html, then falls back toconfig.local.php in the project root if needed.

After a config file exists for the current instance, /install/ locks itselfand will not run again.

The fresh install also creates a sticky starter topic in the default GeneralDiscussion board. Its first setup item is Mail Services, which should beconfigured from Admin -> Mail Services before relying on email verification,password recovery, or future notification mail.

Manual Config Fallback

If PHP can create the database but cannot write the config file, the installershows the generated config content. Put that content in the private config pathshown by the installer.

The generated local config is ignored by Git and is blocked from direct webaccess by the bundled .htaccess rules.

Production Notes

For a production environment that does not live in a path CoreBB can infer,configure the environment explicitly:

COREBB_ENV=live
COREBB_PRIVATE_CONFIG=/absolute/path/to/config.live.php

Local installs generated by the installer are auto-detected from the currenthost and install path. This allows multiple forums on one domain, such as/forum and /community, to use separate private config folders.

You can force a stable instance key when needed:

COREBB_INSTANCE=main_forum

You can also force the private config base directory:

COREBB_PRIVATE_BASE_DIR=/absolute/path/to/corebb_private

Legacy shared private config files directly under corebb_private are stillsupported for root installs. Subdirectory installs use instance-specific configpaths by default to avoid accidentally loading another forum's credentials.

If the web host does not allow PHP to create or write the preferred privatedirectory, the installer writes config.local.php in the project root. CoreBBwill load that fallback as a last resort, and the included .htaccess deniesdirect browser access to config.local.php; a private directory above the webroot remains the recommended layout.

HannsGruber
Posts: 7
Registered: Jun 26
30sf5ms.gif

Open-Source Readability Notes

CoreBB still carries a mix of old forum-era PHP, newer view models, and Twigtemplates. Keep changes easy to review by making the request flow explicit andby documenting why a helper exists, not just what each line does.

For the current request/data path through the project, start withdocs/data-flow.md.

Function Comments

Every new or materially edited named function should have a short block commentimmediately above it:

/**
* Usage: Build the public page model for a board topic list.
* Referenced by: controllers/forum.php?action=board and views/pages/board.twig.
*/
function corebb_board_fetch_model(...) { ... }

Use Usage: for the function's contract. Use Referenced by: when a maintainerwould otherwise have to search to understand the call path. It does not need tolist every indirect caller; name the primary route, model, template, or helper.

Avoid comments that restate the code. A useful comment explains a boundary,permission rule, compatibility reason, or side effect.

Current Public Flow

  • Browser routes start through CookieEngine.php, which applies securityheaders, session state, global CSRF enforcement, and the signed-login cookie.
  • Public route files should gather request data, call a view model or processor,and render through corebb_render_public().
  • Twig carries display HTML for public pages. PHP view models should hand Twigarrays, strings, URLs, flags, and preformatted content models.
  • User-provided post/PM/profile/page content should move throughcontent_format_helpers.php and views/partials/formatted_content.twig.
  • Normal public writes should be POST with corebb_csrf_token; API writes usethe API CSRF header/body token checked in lib/api/guardrails.php.

First-Pass Coverage

This pass documented every named function in the main public request boundary:

  • CookieEngine.php
  • controllers/forum.php
  • controllers/blogs.php
  • lib/security.php
  • lib/view.php
  • lib/blog_helpers.php
  • lib/blog_view_model.php
  • lib/post_view_model.php

The older admin, archive-import, and bulk-maintenance helpers still have mixedcomment styles. When touching those files, apply the same function-comment ruleas part of the change rather than doing a noisy mechanical sweep.