docs/ developer reference

How the studio is built.

Point Edit Studio is a PHP visual web editor — a single-page frontend over a PHP API, backed by SQLite. No build tools, no package managers, no test suite. This guide maps the architecture, data model, API surface, and deployment.

PHP + SQLite Zero dependencies Vanilla JS SPA Self-hosted
01 overview

What it is

A PHP-based visual web editor delivered as a single-page application. The frontend lives in _studio/index.html — vanilla JavaScript and CSS, no framework. It talks to a PHP API backend that reads and writes real files on disk and stores its own state in SQLite.

The defining constraint: nothing to build and nothing to install. There are no bundlers, no package managers, and no test runner. You drop the _studio folder onto any host that runs PHP and it works.

  • Frontend — one HTML file, vanilla JS + CSS, SPA navigation.
  • Backend — a PHP API router (api.php) plus per-feature handler files.
  • Storage — SQLite for app state; edited sites are plain files on disk.
02 layout

Directory map

Everything ships inside _studio/. The router dispatches by an action parameter — roughly forty handlers live inline in api.php, and the rest are split into per-feature files under handlers/.

PathPurpose
api.phpAPI router — dispatches handlers by action; also contains ~40 inline action handlers.
bootstrap.phpSession, auth helpers, site/user CRUD, safePath(), peRemoteCall().
database.phpSQLite ORM — sites, users, activity_log, settings, extension_types, intake_submissions; WAL journal.
config.phpMaster credentials, timeout, allowed extensions. Some endpoints rewrite this file via regex.
totp.phpPure-PHP TOTP (RFC 6238) and QR generator — no dependencies.
handlers/Action implementations: auth, sites, users_handler, files, backups, extensions, git, ga, intake, workflows.
data/pe_app.sqlite (auto-created) and workflows.json.
setup.html + setup.phpFirst-run setup wizard — SPA mode or classic PHP fallback.
library.jsonExtension catalog of installable entries (some flagged coming_soon).
extensions/Installed PHP extension plugins, each with a manifest docblock.
_canvas/ · _workspaces/Empty stubs reserved for future modules.
03 data

Data model

State lives in a SQLite database at data/pe_app.sqlite, created automatically on first use. The older sites.json and users.json files are legacy and no longer authoritative.

Tables

TablePurpose
sitesWorkspaces with environments[] JSON, extensions[], API keys.
usersUser accounts, 4-tier roles, TOTP secrets, site assignments, owner field.
activity_logAudit trail of all user actions.
settingsKey-value config store (allowed_extensions, session_timeout, webhooks).
extension_typesRegistered file-type extensions from plugins.
intake_submissionsPublic client intake form submissions.
client_requestsConnect feature — tickets and projects.
request_messagesMessages on Connect requests.
nexus_serversServer connections for DirectAdmin management.
sessionsDB-backed PHP sessions (id, data, username, ip, user_agent, updated).
rolesCustom role definitions with fine-grained permission grants.

Sites & environments

Each site carries an environments[] array. An environment has a name, a type of local or remote, a root path (local) or url + api_key (remote), and a branch. New sites start with three: live, staging, and local.

environment object
{
  "name":    "staging",
  "type":    "remote",        // local | remote
  "url":     "https://staging.example.com",
  "api_key": "sk-...",          // remote only
  "branch":  "staging"
}

Users & the role hierarchy

Users sit in a four-tier hierarchy. Each user has an owner field linking them to a parent, so resellers manage account owners, who in turn manage sub-users. The top-level admin is the PE_MASTER_USERNAME.

Permissions (RBAC)

Fine-grained role-based access control with ancestor matching. Default roles:

  • admin: * wildcard — full access to everything.
  • reseller: files, backups, git, users, extensions, intake, connect.
  • account_owner: files, backups, git-read, users-create, connect-basic.
  • sub_user: files-read-write, backups, git-read, seo.

Permissions support * wildcards and ancestor matching — files.* automatically grants files.read, files.write, and any future file permission. Custom roles can be created, edited, and deleted by admins; system roles cannot be deleted and the admin role must retain the * wildcard.

tier 1
admin
Full control. The master account.
tier 2
reseller
Manages account owners beneath them.
tier 3
account_owner
Owns sites; manages their sub-users.
tier 4
sub_user
Scoped to assigned sites only.
04 access

Authentication

Login is password-first, then TOTP when required, then a session. TOTP becomes mandatory if the Google Authenticator extension is installed or a TOTP secret is configured.

  • Flow: password → TOTP (if enabled) → session. Session regeneration at both steps prevents fixation attacks.
  • Two-factor: the google-authenticator.php extension makes TOTP mandatory for every user. TOTP enrollment is a two-step process — secret generation with QR code, then code verification before saving.
  • Brute force: five failed attempts trigger a 15-minute lockout (file-based in temp dir).
  • Sessions: custom DB session handler stored in the sessions table. Two-hour timeout (PE_SESSION_TIMEOUT), refreshed on each request. SameSite=Lax, HttpOnly, Secure (proxy-aware via X-Forwarded-Proto/X-Forwarded-SSL).
  • Impersonation: admins and resellers can impersonate other users via the X-PE-Impersonate header. State-changing actions are blocked while impersonating.
  • IP whitelist: per-user IP whitelist — comma-separated with * suffix for subnet matching. Enforced on every authenticated request.
  • Account statuses: users can be active, disabled, locked, or pending. Non-active accounts are blocked and sessions destroyed.
  • Rate limiting: sliding window — 60 requests/minute per IP+action (except check and download_agent). Returns 429 with Retry-After header.

Guards live in bootstrap.php:

bootstrap.php — guards
requireAuth()             // any signed-in user
requireAdmin()            // master account only
requireResellerOrAdmin()  // reseller tier and up
requireActiveSite()       // a site must be selected
05 reference

API reference

Every call is a POST to api.php?action=<name> with a JSON body. Inline handlers in api.php — including submit_intake, workflows, site_summary, site_activity, diff_backup, and the config actions — are routed before the handler-file includes.

Auth

loginlogin_totptotp_enrolltotp_enroll_verifylogoutcheck

Sites

list_sitesswitch_siteadd_siteedit_siteremove_sitecheck_sitesave_environmentremove_environmenttest_site_connection

Users

list_userscreate_useredit_userdelete_userreset_totpassign_sites_to_ownerassign_sub_users_to_site

Files

treereadsaveproxylinked_cssfind_in_filesreplace_in_filesimagesseo_auditlist_templatesread_templatecreateupload_imagedelete_image

Backups

list_backupsread_backuprestore_backupdiff_backup

Git

git_loggit_showgit_statusgit_branchesgit_mergegit_commitgit_pull

Workflows

start_workflowupdate_workflowlist_workflowsclear_workflows

Extensions

list_extensionsinstall_extensionregister_file_typesunregister_file_typesget_registered_typesextension_sitesremove_extension_from_siteenable_extension_on_sites

Google Authenticator

ga_extension_statusga_extension_enablega_extension_disable

Intake

PublicRate-limited
submit_intakelist_intakesread_intakeupdate_intake_statusdelete_intakecreate_account_from_intake

Connect

Staff+Client
create_requestlist_requestsread_requestupdate_request_statusadd_request_messageupdate_request_stagesconvert_to_projectmerge_requestsbulk_update_requestslist_canned_responses

System

Admin
get_system_configsave_system_configactivity_logsite_summarysite_activitydownload_agentget_account_ownersget_ownerslist_active_sessions
Note

submit_intake is the only public action — it's rate-limited and protected by a honeypot. download_agent injects the active site's API key into pe_api.php via regex replace and serves the result as a download.

06 editing

File editing constraints

  • Allowed types: html, htm, css, php by default (PE_ALLOWED_EXTENSIONS), plus any types registered by an extension.
  • Size limit: 2 MB per file (PE_MAX_FILE_SIZE).
  • Path safety: safePath() blocks traversal — a path must resolve inside the active environment's root.
  • Backups: every save writes a .bak into /.pe_backups/ inside the site root (keeps last 5 per file, skipped for live env).
  • File locking: concurrent editing is prevented with per-file locks (5-minute timeout, scoped to site + file + username). Only the lock owner can unlock.
  • Atomic writes: temp file + rename for every save — no partial writes.
  • Traversal: walkFiles() skips dot-files, _studio, and .pe_backups.
07 internals

Config self-modification

Actions like save_system_config and the setup wizard rewrite config.php in place — preg_replace over the define(...) lines, with temporary *.pe_tmp.* files for atomic writes.

⚠ Intentional pattern

The regex rewrite of config.php is by design. Don't refactor it into a different config mechanism — other endpoints depend on the file staying a set of define() statements.

08 remote

Remote agent

The remote agent is a single file, pe_api.php, deployed at the root of each remote client site. It authenticates with an X-PE-API-Key header and lets the studio edit that site as if it were local.

bash
curl -X POST https://client-site.com/pe_api.php?action=tree \
  -H "Content-Type: application/json" \
  -H "X-PE-API-Key: sk-your-secret-key" \
  -d '{}'

Supported actions: ping, tree, read, save, linked_css, find_in_files, replace_in_files, images, seo_audit, and git_log.

09 ship

Deployment

Deploys run over git-ftp via .deploy.sh.

.deploy.sh
bash .deploy.sh prod           # git ftp push
bash .deploy.sh staging        # git ftp push -s staging
bash .deploy.sh init           # git ftp init
bash .deploy.sh init-staging   # first-time staging init
10 hardening

Security

Access is locked down at the web-server level through .htaccess rules.

LocationRule
_studio/.htaccessDenies .json, .bak, and .pe_tmp.* files.
_studio/.htaccessBlocks direct access to config.php and setup.php.
_studio/.htaccessAllows api.php, users.php, and setup.php only with an action param.
data/ · handlers/Require all denied — no direct web access.
Root .htaccessBlocks _partials/ and all .json / .log / .sh / .md files.
Defense in depth

Server rules are the outer layer; safePath(), the allow-listed extensions, and the auth guards are the inner ones. Never expose data/, handlers/, or raw config to the web.

11 support

Connect — ticketing & projects

Connect is the built-in ticketing and project management system. It serves as a knowledge base, support desk, and project tracker — all inside the studio, no third-party tools required.

Tickets & projects

  • Two types: ticket for support requests and project for tracked work.
  • Statuses: new, in_progress, waiting_on_client, resolved, closed — with SLA tracking timestamps (first_response_at, resolved_at).
  • Priority: low, medium, high, urgent — auto-prioritization via configurable rules.
  • Categories: bug, broken_feature, functionality, feature_request, design_ui, performance, seo_content, security, billing, general, tier_2_3, urgent_critical, integration, training.
  • Tags & due dates: JSON tag array per request plus optional due date for deadline tracking.

Conversation threads

Each request carries a thread of messages. When a staff member replies the client is notified by email, and vice versa. The first description becomes the first conversation message automatically.

Project stages & merging

  • Stages: staff can set a JSON checklist of stages on any request to track multi-step work.
  • Convert: tickets can be promoted to projects via convert_to_project.
  • Merge: related tickets can be merged — messages and attachments move to the target, the source is closed with a system note.
  • Bulk ops: batch-update status, priority, assigned_to, and type across multiple requests. Bulk-delete supported.

Canned responses

Pre-written reply templates that staff can insert into conversations. Full CRUD for canned responses — create, update, delete, and list.

Access control

Staff see all requests; clients see only their own. Public access tokens (reply_token and access_token) allow clients to view and reply without logging into the studio. Discord and email notifications fire on status changes and new messages.

12 onboarding

Client intake

The public intake form (/intake) collects structured information from prospective clients and feeds it into the studio for review and account creation.

Submission flow

  • Four-step wizard: business info → contact details → SEO & hosting → access credentials.
  • 30+ fields: business name, URL, phone, email, address, SEO analysis, hosting provider, DirectAdmin credentials, access notes, and more.
  • Rate limited: 3 submissions per 15 minutes per IP address.
  • Honeypot: hidden form field catches bots; HTML sanitization and email validation on all inputs.

Intake management

  • Statuses: newreadarchived. Auto-marked read when opened by staff.
  • Convert to account: a single action creates a user account, a site workspace with a live environment, and optionally clones files from a template site. Auto-generates a username from the business name (with deduplication) and a random 12-character password.
13 servers

Nexus — server management

Nexus manages DirectAdmin server connections from within the studio. It provides a unified view of your hosting infrastructure alongside your workspaces.

  • Server connections: store and manage DirectAdmin server credentials in the nexus_servers table.
  • DNS management: view and edit DNS zones for domains hosted on connected servers.
  • SSL certificates: monitor certificate expiry, issuer, and auto-renewal status across all connected servers.
  • Uptime monitoring: health checks on sites and servers with status dashboards.
  • Unified dashboard: switch between workspace file editing and server management in the same session.
14 plugins

Extensions & hooks

Extensions are PHP plugin files installed into _studio/extensions/. Each plugin declares a manifest docblock and can register custom file types, expose API handlers, and hook into system events.

Installation & security

  • Upload & install: extensions are uploaded via the studio UI. The installer scans for blocklisted PHP functions (eval, exec, system, passthru, shell_exec, popen, proc_open, pcntl_exec, assert, create_function, base64_decode) and rejects any file containing them.
  • Manifest: a docblock at the top of the extension file carries the plugin name and version.
  • Extension library: library.json is a catalog of available extension entries — browse and install directly from the studio.
  • Site filtering: extensions can be enabled or disabled per-site. The * wildcard enables an extension on all sites.

File-type registration

Extensions can register custom file extensions via the extension_types table. Once registered, those file types appear in the file tree and can be edited like any built-in type.

Hook system

Extensions declare hooks with @hook <event> <function> in their docblock. When an event fires, the dispatcher invokes the registered function on each active extension. Hooks are cached for 5 minutes.

available hooks
file_saved       // after a file is saved to disk
file_created     // after a new file is created from template
site_deployed    // after git merge deployment completes

Installed extensions

  • Google Authenticator: adds mandatory TOTP two-factor authentication for all users.
  • TixCo Events & Calendar: event widget and calendar management.
  • AI Page Builder: AI-assisted content generation and page building.

Extensions marked coming_soon in the library include SEO Toolkit Pro, Page Speed Optimizer, Form Builder, and AI Content Assistant.

15 versioning

Git & version control

Every site workspace comes with built-in Git — bare repos are auto-created, file saves are auto-committed, and branches map to environments for a natural push-to-deploy workflow.

Bare repo architecture

When a site is created, a bare Git repository is initialized at _workspaces/<siteId>.git and set as the origin for each environment. This bare repo acts as an internal hosting layer — you never push to an external Git host unless you choose to.

Auto-backup on save

Every file save automatically stages, commits, and pushes to the bare origin: Auto-backup: <path> [<username>]. This gives you a full revision history of every edit, tied to the user who made it.

Branch-per-environment

  • Live: master or live branch.
  • Staging: staging branch.
  • Local: local branch.
  • Git merge: push changes from staging to live by merging branches. The bare repo acts as the intermediary — for remote targets the operation is proxied through the remote agent.

Per-file history & restore

  • File history: view every commit that touched a specific file, across all local environments.
  • Restore: restore any file to its content at any commit hash. A .bak backup of current content is created before restoring.

Conflict handling

Pull operations return conflict information when branches diverge. The merge workflow keeps the bare repo as the single source of truth, minimizing conflicts between environments.

Deploy webhook

When a merge deployment completes (e.g. staging → live), the site_deployed hook fires — extensions can react with cache clearing, build steps, or notifications. A configurable deploy webhook URL can also be called.

No tracking · session cookies only · your data, your server