Installation
The quickest way to a new application is the Anvil AppStarter: a CodeIgniter project with Anvil, Shield, a strict security configuration, scheduled commands, tests and one example resource. Follow its README; it ends with step 6 below. The Anvil Demo shows most features with example data.
This page describes how to add Anvil to an existing CodeIgniter application.
Requirements
Section titled “Requirements”| Software | Version |
|---|---|
| PHP | 8.5 or newer, with intl, mbstring and sodium |
| CodeIgniter | 4.7 or newer |
| MySQL | 8.0 or newer, 8.4 LTS for production |
Anvil brings CodeIgniter Shield, Settings and Queue as dependencies.
1. Add the package
Section titled “1. Add the package”In the composer.json of your application, add a path repository that
points to your local Anvil checkout, and publish the assets after every
install and update:
{ "repositories": [ { "type": "path", "url": "../Anvil", "options": { "symlink": true } } ], "minimum-stability": "dev", "prefer-stable": true, "scripts": { "post-install-cmd": ["@php spark anvil:publish"], "post-update-cmd": ["@php spark anvil:publish"] }}Then require it:
composer require rakoitde/anvil:@devWith symlink: true, changes in the Anvil checkout are visible in your
application immediately. anvil:publish copies the prebuilt CSS,
JavaScript and fonts to public/assets/anvil (Config\Anvil::$assetsPath).
2. Configure the encryption key
Section titled “2. Configure the encryption key”Anvil derives its keys (audit chain, component tokens, …) from the CodeIgniter encryption key. It must be at least 32 bytes long; the install command refuses to run otherwise.
php spark key:generate3. Set up Shield
Section titled “3. Set up Shield”php spark shield:setupThis publishes Config\Auth, Config\AuthGroups and Config\AuthToken
and adds Shield’s routes to app/Config/Routes.php. Self-registration is
off in Anvil applications, so exclude its route:
service('auth')->routes($routes, ['except' => ['register']]);Then adjust the configuration:
| File | Setting |
|---|---|
app/Config/Auth.php |
$views['login'] = '\Anvil\Views\auth\login', $views['layout'] = '\Anvil\Views\auth\layout', $allowRegistration = false, $minimumPasswordLength = 12 |
app/Config/Security.php |
$csrfProtection = 'session', $regenerate = false |
app/Config/Filters.php |
global csrf before, global secureheaders after; Shield’s login throttle in $filters (below) |
.env |
app.CSPEnabled = true |
The Anvil login view adds the single sign-on
buttons and hides the password form when Config\Anvil::$passwordLogin is
false; the layout gives the login, two-factor and password pages the look
of the panels.
$regenerate = false keeps the CSRF token stable within a session, so that
the htmx requests of a page do not invalidate each other.
public array $globals = [ 'before' => [ // The API authenticates with tokens, without cookies or session. 'csrf' => ['except' => ['api/*']], 'invalidchars', ], 'after' => [ 'secureheaders', ],];
public array $filters = [ // Shield's throttle of logins, magic links and the TOTP step (per IP). 'auth-rates' => ['before' => ['login*', 'register', 'auth/*']],];Leave out the api/* exception when you do not use the API.
With a SAML provider, also exclude sso/*/callback: the identity provider
posts its answer without your CSRF token.
See Styling for the Content Security Policy.
4. Create a panel
Section titled “4. Create a panel”php spark make:anvil-panel AdminPanelThis creates app/Panels/AdminPanel.php with the id and path admin. Add
the user and role administration of Anvil to it:
use Anvil\Access\Access;use Anvil\Page\Dashboard;use Anvil\Panel\Panel;
final class AdminPanel extends Panel{ public function id(): string { return 'admin'; }
public function path(): string { return 'admin'; }
public function brand(): string { return 'My App'; }
public function resources(): array { return [...Access::resources()]; }
public function pages(): array { return [Dashboard::class, ...Access::pages()]; }}Register it in app/Config/Anvil.php. This class extends Anvil’s
configuration and holds all your settings:
<?php
namespace Config;
use Anvil\Config\Anvil as BaseAnvil;use App\Panels\AdminPanel;
class Anvil extends BaseAnvil{ public array $panels = [AdminPanel::class];}Panels are registered explicitly; Anvil never scans the file system. More in Panels.
5. Decide on multi-tenancy
Section titled “5. Decide on multi-tenancy”Anvil is multi-tenant by default: every record belongs to a tenant and users only see the tenants they are members of. Keep it on, also with a single organisation: the install command creates one tenant, and adding a second one later is a single command. Switch it off only for applications that will never have tenants, and do it before installing:
public bool $multiTenancy = false;Switching later needs a data migration (anvil:tenancy:enable,
anvil:tenancy:disable), see Without tenants.
6. Install
Section titled “6. Install”php spark anvil:install --tenant "My Company" --email admin@example.comThe command
- checks the encryption key,
- runs all migrations (
migrate --all: Shield, Settings, Anvil and your own), - publishes the assets (
anvil:publish), - creates the first tenant (only with multi-tenancy),
- creates the administrator with a generated password, which is shown once and must be changed at the first login,
- creates the role Administrator with full access and assigns it.
| Option | Default |
|---|---|
--tenant |
Default |
--username |
admin |
--email |
asked for |
--group |
superadmin (Shield group of the administrator) |
--promote |
give an existing user with this e-mail address full access without asking |
The command can be run again: an existing tenant and role are reused. An
existing user only gets full access after a confirmation or with
--promote, so a typo in the e-mail address cannot silently promote
somebody else.
Start the server and sign in at /admin:
php spark serve7. Configure Anvil (optional)
Section titled “7. Configure Anvil (optional)”All options live in Anvil\Config\Anvil; override them in
app/Config/Anvil.php or in .env:
anvil.passwordLogin = falseanvil.mfaRequired = trueSee Configuration.
Production checklist
Section titled “Production checklist”| Topic | Setting |
|---|---|
| HTTPS | Only HTTPS; HSTS at the web server or proxy; app.baseURL with https:// (single sign-on and SAML rely on it) |
| Proxies | Config\App::$proxyIPs with the addresses of your reverse proxies, otherwise rate limits and audit IPs see only the proxy |
| CSP and headers | app.CSPEnabled = true with the policy of Styling; Anvil itself sets X-Frame-Options, X-Content-Type-Options and Referrer-Policy on panel pages |
| CSRF | global csrf filter (except api/* with the API, sso/*/callback with SAML, sso/*/backchannel-logout with an OpenID Connect back channel) |
| Login throttle | Shield’s auth-rates filter on login*, register, auth/* |
| Sessions | Config\Session with a lifetime that matches your policy; database or Redis handler with several servers |
| Cache | a real cache handler (not dummy): rate limits and SSO state live there |
| Keys | encryption.key and anvil.auditKey in a secret store, never in the repository |
| Audit | php spark anvil:audit:verify in monitoring; database user with INSERT/SELECT on anvil_audit_log only |
| Jobs | php spark anvil:prune daily, php spark queue:work anvil for large exports |
| Debug | CI_ENVIRONMENT = production (no toolbar, no error details) |
More in Deployment.