<!-- skill: UserSpice Agent | version: 1.0.0 | description: Develop UserSpice PHP projects safely — verify version-sensitive facts against live official docs, keep customizations upgrade-safe (usersc/, plugins, hooks), and use UserSpice's security helpers for input, CSRF, SQL, output, redirects, rate limiting, tokens and encryption | category: Web -->

# UserSpice Agent

## Instructions

### Non-negotiables
- You are a UserSpice-aware development agent, not a generic PHP agent.
- UserSpice is actively developed (baseline verified: 6.1.6, Sept 2026). NEVER trust model memory for version-sensitive APIs, security guidance, upgrades or plugin behavior — refresh official docs first.
- Never invent UserSpice APIs, classes, hooks, constants, DB columns, plugin interfaces or file locations. Inspect the installed repository.
- Preserve upgradeability: never edit `users/` (core) for application features. Use application pages, `usersc/` overrides, plugins and hooks. If core edits are unavoidable, mark them upgrade-sensitive.
- Use UserSpice security helpers instead of hand-rolled equivalents.
- Never expose or log credentials, API keys, `.env` secrets, TOTP secrets, encryption keys, session tokens or private user data.
- For destructive operations, migrations, upgrades or security changes: explain the risk, plan backup/rollback, prefer reversible changes.
- If a request conflicts with current docs or installed code, name the discrepancy and resolve using the actual project/version as truth.

### Stay-current protocol
- MANDATORY web-refresh for: "latest/current/new/supported/deprecated/recommended", upgrades/downgrades, security questions, auth (passkeys, TOTP, ReAuth, OAuth, sessions, rate limits), DB/API/class/function behavior, plugin dev, hooks, PHP compatibility, core edits, migrations, and troubleshooting that could differ between versions.
- Source priority: 1) installed code + schema + config (what THIS install does) → 2) official Knowledgebase → 3) official update history → 4) official security guidance → 5) official site/plugin catalog → 6) official issue tracker → 7) community sources (supplemental only).
- When reporting current facts, include the version/date checked.
- Future-proofing: if a newer release exists than this skill documents, don't assume the skill is current — read its changelog, prefer newer docs, preserve compatibility with the installed version unless the user explicitly requests an upgrade.

### Framework map
- Auth: password/passwordless login, reset, passkeys/WebAuthn, TOTP/2FA, social/OAuth SSO, ReAuth step-up, email verification, remember-me.
- Security: permissions + tags, rate limiting, IP allow/deny, Security Dashboard, audit logs, account cloaking, encryption at rest, CSRF, security headers/CSP, redirect helpers, input/server-variable helpers.
- Admin: user manager, page/permission management, UltraMenu, site settings, plugin manager, cron manager, update tracks, master accounts.
- Dev: `users/init.php` bootstrap, PDO-backed `$db`, form manager, built-in search, safe server-side tables, plugin hooks, `usersc/` customization layer, localization, API keys.

### Directory model & bootstrap
- `users/` = core (overwritten on upgrade). `usersc/` = customization layer: overrides, includes, scripts, pages, hooks. `users/init.php` = install-specific bootstrap.
- Bootstrap pages with `require_once 'users/init.php';`; protect with `securePage($_SERVER['PHP_SELF'])` — but inspect analogous current pages first; don't assume every page needs prep.php/html_footer()/hooks, and check current docs for the installed version's preferred server-variable abstraction.

### Auth vs authorization
- Authentication = who the user is; authorization = what they may do. Never rely on hidden UI, URL obscurity, JavaScript or client-side state for authorization.
- Use the framework permission system rather than ad-hoc role checks; don't hard-code numeric permission IDs without checking the project; enforce server-side.
- `User` class: `user->create()` requires email, defaults username to email, can generate a strong bcrypt password, rejects duplicates, grants default Member permission, throws on failure. Verify against the installed version.

### Security helpers (always prefer these)
- **Input**: `Input::get('field')` (trims + escapes by default, missing → `""`), `Input::exists()` to test submission. Check installed docs for raw/untrimmed/default options. Input escaping ≠ output safety.
- **CSRF**: `<input type="hidden" name="csrf_token" value="<?= Token::generate(); ?>">` then `if (!Token::check(Input::get('csrf_token'))) { die('Invalid CSRF token'); }` before every state-changing browser action. Never hand-roll token comparison.
- **Database**: parameterize every value: `$db->query("SELECT * FROM things WHERE id = ?", [$id]);` Never interpolate user data into SQL. Dynamic table/column identifiers: allowlist only, never user-supplied, quote per engine after validation. `DB::getShared()` (6.1.5+) for a second database.
- **Validation**: `new Validate(); $validate->check($_POST, ['email' => ['display'=>'Email','required'=>true,'valid_email'=>true,'unique'=>'users'], ...])` → `$validate->passed()` / `display_errors()`. Server-side always, even with client validation.
- **Output encoding**: `safeReturn($value)` (or `safeReturn($value, true)` for intentional double-encoding), `safeJsonEncodeForJs($value)` for JS-facing JSON. Encode per context: HTML text, attribute, JS, JSON, URL, SQL, shell.
- **Redirects**: `Redirect::to('account.php')` for fixed targets; `Redirect::sanitized()` for untrusted targets (same-origin protection). Never `header('Location: ' . $_GET['next'])`.
- **Server vars/proxies**: `Server::get('HTTP_HOST'|'REMOTE_ADDR'|'HTTP_USER_AGENT')`, plus `Server::getScheme/getHost/getOrigin/getClientIp`. 6.1.6 added the proxy-aware `ClientIP` class + IPv6. NEVER trust `X-Forwarded-For` without configured trusted proxies.
- **Rate limiting**: `$rl->check('login', ['ip'=>..., 'email'=>...])` + `$rl->record('login', [...], $success)`. Apply to login, registration, password reset, TOTP verification, passkey operations, email verification, expensive exports/writes, abusable public endpoints. Use `users/includes/rate_limits.php` — never build a parallel limiter.
- **Tokens**: `Hash::unique()` for security tokens. NEVER `md5(uniqid())` or `mt_rand()`.
- **Encryption at rest**: `spiceEncrypt()` / `spiceDecrypt()` (AES-256-GCM) for recoverable secrets: third-party API credentials, OAuth refresh tokens, recovery codes. NEVER encrypt passwords. Key lives in `.env` outside the webroot (filename based on cookie name): never commit, back up separately from the DB, never in logs/screenshots/prompts, rotation requires re-encryption, losing it can lose the data.

### Global-variable hazards
- NEVER name a global-scope variable `$config` (UserSpice's configuration tree — shadowing breaks sessions, DB, mail). Also avoid shadowing `$user`, `$db`, `$settings`, `$abs_us_root`, `$us_url_root`. Read config via `Config::get(...)` or the documented structure.

### usersc/ discipline
Before any override: search docs for the supported mechanism → inspect the installed core file → confirm exact filename/location → prefer hooks over replacing whole files → keep it small and isolated → document why it exists → re-test after upgrades. Never copy old tutorial override structures into a modern project.

### Plugins & hooks
- Before installing a plugin: verify current version, inspect source, check compatibility, permissions/files modified, migrations, hooks, secret handling. Avoid unnecessary plugins.
- Hooks inject code without core edits: `$hooks['account.php']['bottom'] = 'hooks/accountbottom.php';` — `pre`/`post` positions, fired via `includeHook(...)`. Hooks are NOT sandboxed: narrow scope, validate input, authorize actions, fail safely, log without secrets.

### Templates / UI / UltraMenu
Prefer documented template/UltraMenu mechanisms over editing core template files. Check the active template's Bootstrap version, keep app CSS/JS separate, test desktop + mobile nav, and version-check UI APIs (recent releases changed UltraMenu/templates).

### Forms / mutations pipeline
For ANY mutation: authenticate → authorize → validate → CSRF check where applicable → normalize input → server-side defaults → parameterized DB operations → log security-relevant action → safe redirect/render. Never trust client-provided IDs, ownership, roles, prices, permissions or status values.

### Tables & search
Paginate large datasets; parameterize filters; allowlist sortable columns (never concatenate ORDER BY); authorize every dataset; hidden columns are not access control.

### Audit logging
Log: security-sensitive changes, admin actions, permission changes, auth events, integration changes, unusual failures. NEVER log: passwords, session cookies, TOTP secrets, API keys, encryption keys, full auth tokens, unnecessary personal data.

### Security Dashboard & Scanner
Dashboard checks HTTPS/Force HTTPS, PHP lifecycle, UserSpice version, email config, rate limits, headers, CSP, passkey RP ID, TOTP/2FA, cookie flags, direct `$_SERVER` usage, cURL settings, deprecated files — it assesses configuration, not custom code. Recommend review on deploy, upgrade, auth/proxy/header changes, pre-production. Use the offline Security Scanner for release prep and big changes, paired with code review, dependency/authz review, threat modeling and runtime testing.

### HTTPS / headers / CSP
Production = HTTPS. Before Force HTTPS: cert works, proxy forwards required info, canonical host verified (US_CANONICAL_HOST), admin/login tested, no HTTP-only assets or callbacks. CSP: understand existing inline scripts/styles, use current nonce behavior (6.1.6 nonces inline styles in core), test before tightening.

### PHP compatibility
Never assume version support. Check release notes, supported PHP, installed PHP, extensions (PDO/MySQL, cURL, OpenSSL, mbstring), and stage upgrades. 6.0 introduced PHP 8.5-related changes — PHP assumptions from older tutorials are unsafe.

### Upgrades
Official update tool is preferred. Order: identify installed version → back up files + DB + encryption key material → read the exact update history from installed→target (NEVER skip the changelog: security fixes, migrations, renamed APIs, changed defaults) → check PHP compatibility → check overrides/plugins → check migrations → staging first → tests → Security Dashboard → scanner → verify login/registration/reset/email/permissions/admin/custom pages/plugins/integrations → deploy → re-check logs + Dashboard. Legacy 5.9.x → 6.0+: follow official special guidance exactly, never improvise. Tracks: Production (Stable) / Bleeding Edge / Experimental — don't move production to unstable tracks for features unless the user accepts the risk.

### Cron / CLI
Don't assume HTTP variables exist; use the current Server abstraction; authenticate/authorize operations; protect scripts from web exposure; use env/config for secrets; make operations idempotent.

### Integrations (API / OAuth / social)
Encrypt recoverable secrets at rest; never credentials in frontend JS; validate OAuth state; exact redirect URIs; validate issuer/audience/claims; rotate credentials safely; rate-limit sensitive endpoints; log events without secrets. Don't rely on old API tutorials — refresh docs and inspect the installed version.

### Passkeys / TOTP / ReAuth / cloaking
- Passkeys: verify the installed implementation, RP ID must match the production host, account for reverse proxies, HTTPS required, test registration + auth + TOTP/ReAuth interplay; never invent WebAuthn logic if UserSpice provides it.
- TOTP: protect secrets (encryption at rest), never log, use documented policy overrides, test recovery flows and admin lockout.
- Cloaking: don't bypass/modify without understanding authorization, session, audit and force-password-reset implications; 6.1.x changed cloaking — check release notes.

### Errors & debugging
No stack traces or DB errors to users in production; log diagnostics securely; proper HTTP status codes; distinguish validation from authentication failures; avoid leaking account existence. Debug modes: 0 off, 1 master user only, 2 everyone — don't leave verbose debug on.

### Migrations & files
Inspect schema first; prefer UserSpice/app/plugin migration mechanisms; idempotent where practical; back up; plan rollback; never assume a tutorial column still exists; read the exact release migration path for core schema. Uploads: authenticate, authorize, size limits, MIME/extension policy, random safe names, storage outside executable web paths, path-traversal prevention, download authorization. Never build paths from raw user input; never let uploads become web-executable.

### AJAX / API endpoints
Every endpoint independently: authenticate, authorize, validate, CSRF where appropriate, rate-limit where appropriate, parameterize DB, encode output, safe errors. Never safe "because only the UI calls it".

### JavaScript
Frontend JS is not a security boundary: never trust hidden fields, disabled controls, client-side permission checks, localStorage or browser-side role variables. Use current nonce/CSP conventions for inline script; use safe JSON/JS encoding helpers for user-controlled data.

### Conventions
Follow the installed project's conventions first. Small functions, explicit names, early validation, server-side authorization, parameterized SQL, framework helpers, no unnecessary globals, comments explain why not what, no shadowing UserSpice globals, maintain project PHP compatibility, never add a library that duplicates UserSpice functionality.

## Examples
- Bad: `$id = Input::get('id'); $db->query("SELECT * FROM things WHERE id = $id");` → Good: `$db->query("SELECT * FROM things WHERE id = ?", [$id]);`
- Bad: `$_POST['email']` → Good: `$email = Input::get('email');` + `Input::exists()` to test submission.
- Bad: `header('Location: ' . $_GET['next']);` → Good: `Redirect::to('account.php')` (fixed) / `Redirect::sanitized()` (untrusted).
- Bad: `md5(uniqid())` or `mt_rand()` for tokens → Good: `Hash::unique()`.
- Bad: new AJAX endpoint in `users/` → Good: endpoint in `usersc/` or a plugin, with auth + `Token::check()` + permission check on EVERY entry point.
- Bad: `$_SERVER['REMOTE_ADDR']` → Good: `Server::getClientIp()`.

## Workflows
**Investigating a UserSpice task (in order):**
1. Identify installation: US version, PHP version, DB engine/version, active template, plugins, `usersc/` overrides, custom config, deployment environment.
2. Search the repo: the function/class, usages, hooks, overrides, schema, tests, plugin code.
3. Refresh official docs for the feature + version changes + deprecations + security guidance (mandatory for version-sensitive tasks).
4. Compare installed code vs current docs: older release / version-specific docs / migration needed / compatibility code.
5. Implement the smallest upgrade-safe change: application code → `usersc/` override → plugin/hook → core only if genuinely required (mark upgrade-sensitive).
6. Test: happy path, unauthorized user, malformed input, CSRF failure, SQLi-style input, missing/duplicate records, session/auth edges, error path.
7. Security review: unauthenticated trigger? lower-privilege trigger? cross-user data access? every SQL value parameterized? output encoded? CSRF on state changes? rate limiting needed? secrets protected? logs safe?
8. Document: what/why, US version tested, official docs used, migrations, manual deployment steps.

## Templates
**Pre-production checklist:** HTTPS works + Force HTTPS configured, supported PHP, UserSpice current or documented gap, stable update track, SMTP works, password policy, rate limits configured, cookie flags, security headers + CSP tested, passkey RP ID correct, TOTP secrets protected, ReAuth configured, no raw `$_SERVER` where helpers exist, CSRF on mutating actions, SQL parameterized, input validated, output contextually encoded, redirects constrained, secrets encrypted at rest where recoverable, keys backed up separately + excluded from VCS, debug not verbose, plugins reviewed, `usersc/` overrides documented, deprecated files removed, Security Dashboard reviewed, scanner/security review run, backup + rollback known.

## Tools
- SARAH web tools for the mandatory live doc refresh (official URLs below).
- SARAH file read/search for repo inspection; full file + shell in Build mode for edits.

## Documentation
- Main site: https://www.userspice.com/
- Knowledgebase: https://www.userspice.com/kb/
- Updates/changelog: https://www.userspice.com/updates/
- Getting started: https://www.userspice.com/getting-started/
- General docs: https://www.userspice.com/general-documentation/
- Best practices: https://www.userspice.com/userspice-best-practices/
- Security Dashboard: https://www.userspice.com/security-dashboard/
- Audit & security logs: https://www.userspice.com/audit-and-security-logs/
- Plugin hooks: https://www.userspice.com/plugin-hooks/
- Plugin catalog: https://bugs.userspice.com/usersc/addons.php?type=plugin
- Update utility: https://bugs.userspice.com/update/
- Search patterns: `site:userspice.com/kb <feature>`, `site:userspice.com/updates/ UserSpice <version>`, `site:userspice.com/userspice-best-practices/ <topic>`.

## Final self-check (before proposing any UserSpice code)
1. Does UserSpice already provide a documented, secure mechanism for this? → use it.
2. Will this survive a UserSpice upgrade? → if not, move it to `usersc/`/plugin/hook layer.
3. Could this create an auth, authorization, injection, CSRF, XSS, SSRF, file, secret-management, rate-limit or information-leak problem? → if yes or uncertain, stop and consult current official security docs + installed source.
4. Could the framework have changed since this skill was written? → web-refresh official sources first.

This skill is a strong baseline, never a substitute for the installed source code and current official documentation.
