What you don't have to build.

Every serious web app needs the same foundation. Joinery ships it all.

Auth, sessions, and user management — done.

Login and logout. Password reset with secure time-limited tokens. Email verification on registration. Role-based access control on a 1–10 permission scale.

Login / Logout

Persistent sessions, secure cookie handling, remember-me support.

Password Reset

Secure time-limited tokens sent via email. Argon2id hashing throughout.

Email Verification

On registration and email changes. Configurable enforcement.

Permission Levels

1–10 scale (member through superadmin). Restrict any page or endpoint with one call.

Subscription Tiers

Feature-gating tied to billing. Upgrades and downgrades handled automatically.

Groups & Directories

Group membership, member directory with search, activity tracking.

Active Record with zero boilerplate.

Define a class, get a full CRUD interface. Declare your fields and the table is created and kept in sync automatically.

// Load a record $user = new User($id, TRUE); $email = $user->get('usr_email'); // Save changes $user->set('usr_name', 'New Name'); $user->save(); // Query multiple records $users = new MultiUser( ['usr_active' => 1], ['usr_created' => 'DESC'] ); $users->load();

Automatic Table Management

Define fields in the class. The table is created on deploy. Schema changes picked up automatically.

Multi-Object Collections

Filterable, sortable queries with pagination. No SQL boilerplate for standard operations.

Soft & Hard Delete

Soft delete with cascade support. Permanent delete with configurable cleanup actions.

Stripe and PayPal, without the integration work.

Subscription billing, one-time products, coupons, webhooks — all built in. Zero platform fees.

Stripe Subscriptions

Recurring billing, trial periods, proration. Webhook handling for all payment events.

One-Time Products

Product catalog with multiple price types, order history, and management UI.

Coupons & Discounts

Percentage and fixed discounts. Single-use or multi-use. Time-limited or permanent.

PayPal Integration

PayPal as an alternative payment method alongside Stripe.

0% Platform Fees

You keep 100% of what your payment processor pays you. No platform tax.

Webhook Handling

Stripe and PayPal webhooks handled automatically for payment lifecycle events.

Send email without a third-party dependency.

Mailgun for deliverability, or any SMTP server via PHPMailer. Swap the provider in config without touching application code.

Mailgun Integration

API-based sending with delivery tracking and bounce handling.

Any SMTP Server

PHPMailer under the hood. Works with any provider — or your own mail server.

Transactional Templates

Reusable email templates for confirmations, resets, and notifications.

Newsletter Sending

Mailing list management and bulk sending with subscriber management.

Every model gets an admin interface.

The admin dashboard follows consistent patterns across every feature. It's not a scaffold — it's a real, functional interface that runs the same system it's managing.

List Views

Search, filters, and sortable columns. Bulk operations for common actions.

Detail Views

Edit forms built with the same FormWriter system you use in your app.

Analytics & Reporting

Built-in reporting views for users, revenue, events, and activity.

Settings Management

All system settings editable from the admin. Plugin settings declared in plugin.json.

A full REST API, already wired up.

Every data model is accessible via the API. Key-based auth, rate limiting, CORS, JSON in and out.

# List users curl -H "X-API-Key: your_key" \ https://yoursite.com/api/users # Create a product curl -X POST \ -H "X-API-Key: your_key" \ -H "Content-Type: application/json" \ -d '{"pro_name": "Premium Plan"}' \ https://yoursite.com/api/products

Key-Based Auth

Generate API keys per user. Scoped permissions and rate limiting per key.

40+ Endpoints

CRUD plus action operations across all core models.

Extend in Plugins

Add custom endpoints in your plugin. Same auth and routing infrastructure.

Add features without touching core.

Plugins are self-contained modules with their own data models, views, admin pages, routes, and scheduled tasks. Activate and deactivate without data loss.

plugins/my_feature/ plugin.json # metadata, settings, version data/ # data model classes (tables auto-created) views/ # public-facing views (routes auto-discovered) admin/ # admin interface pages logic/ # business logic tasks/ # scheduled tasks assets/ # CSS, JS, images

Auto-Discovered Routes

Create a view file in your plugin and the route works immediately. No config.

Own Data Models

Plugin tables created automatically. Schema changes picked up on deploy.

Activate / Deactivate

Enable and disable plugins without data loss. Settings preserved between states.

Your License

Plugins you write are your code. Release under MIT, keep proprietary, or sell commercially — your choice.

Replace the entire UI without forking anything.

The override chain lets you swap any view, template, or asset at the theme level without modifying core files or plugin files.

Override Chain

theme → plugin → base. Customize any view at the theme level — it overrides downstream automatically.

Framework Choice

Bootstrap, Tailwind, or zero-dependency HTML5. FormWriter and the system adapt to whichever you choose.

Component System

Reusable page sections stored in the CMS. Editable by admins without touching code.

Security is structural, not optional.

Most frameworks give you security tools and expect you to remember to use them. In Joinery, the secure path is the only path.

SQL Injection

PDO prepared statements everywhere. No string concatenation paths exist in the codebase. The model layer is structurally incapable of passing raw input to the database.

XSS

All output through FormWriter is escaped automatically. View templates use htmlspecialchars(). You can't forget — the helpers do it.

CSRF

FormWriter generates and validates CSRF tokens on every form. Zero extra setup required.

Passwords

Argon2id. Not bcrypt, not MD5. The current best practice. Legacy bcrypt hashes auto-upgrade on next login.

Cookies

HttpOnly, SameSite=Lax, Secure. Session cookies are inaccessible to JavaScript.

File Uploads

Validated by type and size, stored outside web root, served through controlled handlers — not direct URLs.

Everything here is in the first commit.

Install Joinery and all of this is already working.