Install

Self-host it in about fifteen minutes.

Docker Compose brings up FrankenPHP and PostgreSQL together. No local PHP needed — the backend, the tests and the CLI all run inside the container.

Before you start

What you need.

Docker + ComposeStarts PostgreSQL and the FrankenPHP app together. The recommended local setup.
ComposerRun on the host, before the stack starts. See step 2 — this is the one that bites.
Node.jsFor the Next.js admin client only. The backend does not need it.
PHP 8.4Inside the container (dunglas/frankenphp:1-php8.4). You do not need it locally.
PostgreSQL 15The only supported database. Compose starts it for you.
Quick start

Five steps.

Copy each block in order. The whole thing takes about fifteen minutes on a warm Docker cache.

  1. Clone it

    1 · clonebash
    git clone https://github.com/AmroKSaleh/whity-core.git
    cd whity-core
  2. Install PHP dependencies — on the host, first

    The development image deliberately ships no vendor/, and Compose bind-mounts your checkout over /app — so the container runs against whatever vendor/ your working copy has. If that is nothing,db-init exits 255 and the stack never comes up.

    2 · dependenciesbash
    composer install
    On Windows, mirror the SDK instead of symlinking it. The plugin SDK is a Composer path repository, which Composer satisfies with a symlink — and a symlink made on a Windows host does not survive the bind mount into a Linux container, so every Whity\Sdk\* class fails to autoload. Copy it instead:
    windowsbash
    COMPOSER_MIRROR_PATH_REPOS=1 composer install
    Re-run it after changing anything under sdk/.
  3. Start the stack

    Set JWT_SECRET and ENCRYPTION_KEY in .env first if you are running anything other than APP_ENV=development — production fails fast on a missing or weak value rather than booting insecurely.

    3 · startbash
    docker compose up -d
  4. Create the schema

    migrate run already creates the bootstrap administrator, soseed is optional. Add it for the default tenant, roles and notification-template baseline.

    4 · migratebash
    docker exec whity_frankenphp php public/index.php migrate run
    
    # optional: default tenant, roles, notification templates
    docker exec whity_frankenphp php public/index.php seed
  5. Start the web client

    5 · webbash
    cd web
    npm install
    npm run dev

    API on localhost:8000, admin UI on localhost:3000, health atlocalhost:8000/api/health.

There is a shortcut for steps 3 and 4 once dependencies are installed:make setup runs docker compose up -d followed by the database initialisation script.

Onboard your agent

One line. Then it knows the rules.

Paste it into Claude Code, Cursor, or whatever you build with. Your agent fetches the setup instructions, works out whether it is looking at the core platform, a plugin project or an empty directory, and writes the project's conventions into your agent instructions file so they survive the session.

You can read exactly what it will execute first — the instructions are a plain markdown file, not a script.

Read the instructions →

Copies one line to your clipboard

Fetch and execute the appropriate instructions to set me up for Whity from https://whity.dev/agent-setup/prompt.md
Going further

Let it drive a real instance.

The line above is enough for an agent writing code. To let one operate a running Whity — create a tenant, assign a role, file a form — connect it to that instance's MCP endpoint. It gets exactly the permissions its token carries, enforced by the same component that guards the HTTP API.

Whity is self-hosted, so the endpoint lives on your own instance; there is no shared server to point at. Issue a token with a human access token, then configure your client.

Full connection guide → · How the tools are derived →

1 · issue an agent tokenbash
curl -s -X POST https://your-whity-host/api/mcp/tokens \
  -H "Authorization: Bearer <your-access-token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-agent", "scope": ["tools:call"]}'
2 · point your client at itjson
{
  "mcpServers": {
    "whity": {
      "url": "https://your-whity-host/mcp",
      "headers": {
        "Authorization": "Bearer <mcp-token>"
      }
    }
  }
}
Accounts

Who you log in as.

Every environment gets a bootstrap administrator — system@whity.local by default, or whatever INITIAL_SYSTEM_ADMIN_EMAIL names. Set that to a mailbox you actually control: the default is unroutable, so it can never receive a password reset.

The *@example.com demo logins exist only underAPP_ENV=development, and only when you pass --with-fixtures toseed. Seed passwords are env-configurable; absent an env value a random one is generated and printed once.

Questions

Install FAQ.

What do I need to run Whity?

Docker and Docker Compose, Composer on the host, and Node.js for the web client. No local PHP is required — the backend, tests and CLI all run inside the php:8.4 / FrankenPHP container. PostgreSQL 15 is the only supported database and Compose starts it for you.

Why does the stack fail with 'vendor/autoload.php not found'?

Because composer install has to run on the host before docker compose up. The development image deliberately ships no vendor/ directory, and Compose bind-mounts your checkout over /app, so the container runs against whatever vendor/ your working copy has. If that is nothing, db-init exits 255. Only the release image target runs composer install for you.

Why does Whity fail to autoload Whity\Sdk classes on Windows?

The plugin SDK is consumed through a Composer path repository, which Composer satisfies by symlinking vendor/whity/plugin-sdk to the sdk directory. A symlink created on a Windows host does not survive the bind mount into a Linux container. Run COMPOSER_MIRROR_PATH_REPOS=1 composer install so Composer copies the SDK into vendor/ instead, and re-run it after changing anything under sdk/.

Do I need to run seed after migrating?

No. migrate run already creates the bootstrap administrator, so seed is not required for a working install. Run seed to add the default tenant, roles and notification-template baseline, and seed --with-fixtures for the demo logins, which exist only under APP_ENV=development.

Now run something.

The CLI handles migrations, tenants, plugins, translations and the background workers.