Getting started
Tiger is a multi-tenant SaaS foundation — orgs, users, memberships, roles, auth, an admin back
office, a CMS, an API layer, and theming, already built. You add features as modules; the
framework lives in vendor/ and updates like any Composer package without ever eating your work.
This page is the orientation lap: what you get, then a concrete first ten minutes. It's not a reference — every section points at the guide that goes deep.
Install it
One composer create-project, then four console steps — the whole thing is on one page:
Install in 60 seconds. Come back here when install:admin has printed
your founding account.
What you get before you write a line
| Multi-tenancy | org / user / org_user — the membership row is both the tenancy boundary and the role carrier (more) |
| Auth | email-or-username login, peppered password hashing, self-service reset, passwordless code login, TOTP 2FA, DB-backed sessions, server-authoritative auto-logout (more) |
| Authorization | a deny-by-default Zend_Acl graph enforced by a front-controller plugin you can't forget to inherit (more) |
| Webservices | one /api endpoint; shipping an endpoint means writing a method (more) |
| Admin back office | a themed shell with Content, Users, Organizations, Modules, Updates, and Settings screens (more) |
| CMS | database-driven pages / layouts / partials, versioned on every save, schedulable, tenant- and locale-aware (more) |
| UI | vendored Bootstrap 5 — no npm, no build step — with runtime CSS-variable skins and light/dark (more) |
| Config | a four-tier cascade whose bottom tier is a DB table, so settings change live with no deploy (more) |
Everything above is substrate. Your job starts at the module.
Your first ten minutes
1. Sign in
bin/tiger install:admin created your founding org and its owner. Sign in at:
/auth/login
Email or username, plus the password you set. (If you skipped install:admin, run it now — Tiger
deliberately ships with no default account.)
2. Look around the back office
/admin is the dashboard: a grid of widgets that modules register (Tiger_Dashboard), reordered
and collapsed per user. A fresh install's dashboard is sparse — that's correct, nothing is hardcoded.
The sidebar is ACL-filtered live, so you only see what your role can actually reach (and only what's active — most of these are first-party modules):
| Sidebar item | Path | What it manages |
|---|---|---|
| Dashboard | /admin |
your widget grid |
| Content | /cms/page |
CMS pages, layouts, partials |
| Articles | /blog/post |
blog posts |
| Menus | /cms/menu |
the drag-drop menu builder |
| Media | /media |
uploads and storage |
| Users | /access/user |
people (everyone) |
| Organizations | /access/org |
tenants |
| Code | /code |
the Code Area (PHP/CSS/JS snippets) |
| Modules | /system/modules · /system/updates |
install/activate, and updates |
| Settings | — | each module's settings page, ACL-filtered |
The header carries site search, the language switcher, a light/dark toggle, the skin switcher, and
your user menu. /account is the other surface — the same shell, but the menu is your stuff
(profile, your organization) instead of the platform's. Admin manages everyone; the account surface
manages yourself.
3. Publish a page
Go to Content → New. The editor asks for the things that actually matter:
| Field | Notes |
|---|---|
title / slug |
the slug is the public URL — /pricing |
type |
page, layout, partial, or block |
format |
html, markdown, phtml, or builder (the drag-drop editor) |
status |
draft / published / archived |
published_at |
leave blank to publish now, or set a future time to schedule it |
Save, then visit /your-slug — the front controller resolves an unclaimed URL to a published page.
Every save snapshots a version you can restore, and renaming a slug leaves a 301 behind
automatically. Details in CMS → Pages.
Want it at /? The admin Settings screen sets the site name and which page is served as the home
page — those are rows in the config table, so the change is live on the next request with no deploy
(Settings).
4. Reskin it
Open the skin switcher in the header and pick another palette. It hot-swaps with no reload,
because a skin is nothing but a :root { --bs-* } CSS-variable overlay — no recompile, no
node_modules. Skins resolve per-user (a cookie) and per-org (a config row), which is also how
tenant branding works. See Theming & skins.
5. Scaffold a feature
vendor/bin/tiger make:module billing
vendor/bin/tiger module:activate billing
You now have a Bootstrap.php, a live controller, an example /api service, its acl.ini /
routes.ini / module.ini / dependency.ini, a view script, and empty models/, migrations/,
and assets/ folders in application/modules/billing/. It's reachable at /billing/ immediately —
zero route registration, zero core files touched — and the scaffolded service answers a call
right away:
fetch('/api', { method: 'POST', body: new URLSearchParams({
module: 'billing', service: 'example', method: 'ping',
})}).then(r => r.json());
Activation is zero-infrastructure: no Apache/nginx edit, no DNS. Add a method to the service and it's callable — that's the whole loop.
Where to go next
- Core concepts — the vocabulary and mental model to have before you
write code: the four layers, ownership, tenancy, the config cascade, the
/apimessage. - Your first module — the five-minute philosophy tour, then How to write your first module for the full walkthrough (table, service, ACL, settings, clean uninstall).
- Building on Tiger — the hub: which guide covers which step.
- Admin — running the site from the back office (no shell required).