Panel CommentsAdmin sign in
Documentation

Architecture

Architecture

Panel Comments is a conventional PHP application with physical, rewrite-free web entry points and a small service container:

panel-comments/
├── index.php                 installation entrance/public landing
├── install/index.php         first-run setup
├── admin/index.php           administration gateway
├── api/index.php             public API gateway
├── docs/, llms.txt, SKILL.md public human and agent documentation
├── integration.php           safe public discovery manifest
├── embed.js, style.php, assets/ drop-in client and website CSS
├── panel-config.php          optional/generated infrastructure config
├── panel-content/            guarded writable runtime storage
├── bin/panel                 CLI and optional cron
├── config/defaults.php       non-secret defaults
├── src/                      application source
└── vendor/                   bundled production dependencies

The application root is the website directory. Physical gateways translate stable action query values into internal routes, preserving centralized controllers and error handling without requiring Apache/Nginx rewrites. PublicUrl generates every public path and supports both domain-root and subfolder installs.

The authenticated admin shell resolves one capability-aware context containing the administrator, allowed websites, persistent selected website, and per-site role. Installation-wide tasks remain in the thin global menu; website work is rendered from the selected context in the left sidebar. The public docs renderer and Admin Help share a fixed catalog of bundled Markdown files. The public integration manifest is deliberately database-independent and never lists configured websites or settings.

The classic embed.js loader discovers the API from its own URL, reads the canonical/current embedding page, and loads the widget as a classic cross-origin script. By default the custom element mounts isolated built-in CSS in Shadow DOM, followed by one versioned per-website stylesheet containing the selected layout preset, configured colors, and optional custom CSS. The immutable URL changes whenever website settings change. A per-website setting instead renders light-DOM HTML without requesting either stylesheet, allowing the embedding website to style it directly. API responses enforce the exact configured website address through CORS.

Bootstrap is personalized and never shared. It issues a deterministic signed read-version token over website, discussion content, engagement, ranking, and serializer versions. Public thread, search, load-all, and reply reads carrying the current token are safe for application and CDN caching; tokenless clients remain compatible but receive no-store. Mutations advance the relevant version and return a replacement token. Filesystem single-flight locks collapse concurrent misses, and external cache failures fail open to authoritative database reads.

Every API write is bound to the website, page, visitor, action, configurable randomized honeypot, and one-use form ticket. Comment creation can additionally require a signed first-party cookie round trip. Turnstile is an optional extra layer. Client UUIDs make comment creation idempotent.

MySQL/MariaDB is authoritative. Public reads use content/engagement/ranking cache versions, weak ETags, keyset pagination, per-root reply previews, indexed queries, and denormalized counts. Reaction actor rows retain one privacy-preserving browser/identity key per target/type while compact aggregate tables make public article and batched comment counts cheap. Existing Like rows migrate in place as thumbs-up reactions so old clients and sort-by-top remain compatible. Discussion search stays zero-configuration by bounding literal substring matching to one page and returning the result count in the same query. Large discussions use a separate stable-ID, 200-record flat stream that the widget reconstructs and renders in small animation-frame chunks. Filesystem caching is the default; APCu, Redis, Memcached, and Elasticsearch are optional.

Comment Markdown is rendered and sanitized on the server, stored with an explicit renderer version, then filtered again by a small browser DOM allowlist. Preview calls use that same renderer. New renderer versions can be applied to old comments in bounded, resumable batches without changing their source Markdown or revision history.

Optional Gravatar images are fetched only by the Panel Comments server. Positive results are cached for seven days, missing images are negatively cached for six hours, stale images are served during upstream failures, and per-image locks prevent duplicate fetches. Protected cache files live under panel-content/avatars/; the browser receives only the local avatar.php URL.

Opt-in remote anti-spam feeds follow the same local-cache boundary. Guarded compiled IP ranges and domain entries live under panel-content/blocklists/; public submissions are matched on the Panel Comments server and are never sent to feed operators. Source refresh is a background job with a bounded automatic fallback when cron is absent.

Mail, webhook fan-out, search indexing, blocklist refresh, and cleanup use a durable outbox with SKIP LOCKED, retries, and stale-lock recovery. A bounded post-response runner provides zero-cron operation; bin/panel cron is an optional throughput upgrade.