Repository navigation
Releases
ShipKit 5.1.0
ShipKit 5.1.0 hardens the MCP server shipped in 5.0.0 after an independent review, and records where a change came from in the logs. If you use 5.0.0, update.
Requires shipwithian/shipkit-core 5.1. A project created from 5.0.0 updates the package with composer update shipwithian/shipkit-core; the template's own changes are code you own, so UPGRADE.md lists what to copy.
Security
- AI client registration only redirects to trusted places. 5.0.0 accepted any redirect address, so any site could register as "Claude" and collect a user's consent. A self-registering client may now redirect only to claude.ai, chatgpt.com, or localhost. Add more origins with
MCP_REDIRECT_DOMAINS(a*is ignored). The consent screen now shows where the app will return the user. - Registration fails closed. Registering an AI client and limiting it to the
mcp:usescope is one transaction, rolled back on an error response, a missing client id, or a failure to limit it, so a client can never exist with unrestricted scopes.
Fixes
- Confirmation: a repeated confirmation that arrives while the first is still running answers "still being processed" instead of an internal error, and the lock lasts at most two minutes (it was ten seconds). A call carrying a confirmation token always goes through the confirmation, so a replay returns the first result instead of running again.
update_my_profileis marked destructive and confirms an email change, naming every change in the summary, before applying it. Its tool name, title, and annotations are explicit.
Logs
- A profile change now records whether it came from the web, the API, or an AI client (with the client's name):
UpdateProfileActiontakes theChanneland logs it.ShareChannelshares only the trustworthy client id; the name an AI client gave itself is looked up for logs.
For agents and maintainers (shipkit-core 5.1)
- Stricter MCP tool checks. A tool is now found by what it extends, not its file name, and needs a snake_case
#[Name], a#[Title], an explicit read-only or destructive annotation, and registration in a server. It must authorize withGate::forUser($user)->authorize()before anything but$user = $request->user();. This is a minor release because the checks apply only to MCP tools, which no project had before 5.0.0, and ShipKit's own tools pass. make:feature {Name} --mcpworks on a feature that already exists, and adds the tool path and glob to its rule file and rules index.ChannelKindreplaces theChannel::WEB,API, andMCPconstants.
Full details: CHANGELOG.md and shipkit-core's changelog.
ShipKit 5.0.0
ShipKit 5.0.0 replaces Sanctum with Laravel Passport and turns every ShipKit app into an OAuth 2.1 server and an MCP server, so users can connect AI clients such as Claude Code, claude.ai, and ChatGPT, and other apps, to their account with their consent.
Requires shipwithian/shipkit-core 5.0. Upgrading from an earlier release: existing API tokens stop working, because Sanctum tokens mean nothing to Passport. UPGRADE.md has the steps. Starting a new project needs nothing: laravel new my-app --using=shipwithian/shipkit installs the latest release.
Use 5.1.0 or later. 5.0.0 accepted any redirect address when an AI client registered itself, so any site could register under a trusted name such as "Claude" and ask a user for consent. 5.1.0 fixes that and several smaller gaps found in review.
What's new
Passport replaces Sanctum
- The
/api/v1endpoints keep their requests and responses, but issue Passport personal access tokens (longer, signed tokens), require theapiscope on every authenticated route, and identify tokens by a string id. PERSONAL_ACCESS_TOKEN_LIFETIME_DAYS(90 by default) replacesSANCTUM_TOKEN_EXPIRATION.
An OAuth 2.1 server
- Other apps ask a user for access through
/oauth/authorize: sign-in first (with two-factor authentication), then a consent screen in the app's own layout. - Only the authorization-code (PKCE for public clients), refresh-token, and personal-access grants are enabled. Operators create clients with
php artisan passport:client.
Settings → Security
- A new "Personal access tokens" section: create (the secret is shown once), list, and revoke.
- A new "Connected apps" section: approved apps with when each was last used, and a button to disconnect one.
Revocation
- A password reset, on the web or the API, revokes every personal access token and every connected app's tokens. Deleting an account deletes them.
An MCP server at /mcp
- AI clients discover the app's sign-in, register themselves (rate-limited and limited to the
mcp:usescope, so they never reach/api/v1), and connect through the consent screen. - The first tools are
show_my_profile,update_my_profile, andlist_roles. Tools act as the signed-in user, through the same policies, Actions, and Queries as the web app. Personal access tokens work on/mcptoo. - A tool marked
#[IsDestructive]returns a summary and a confirmation token first and acts only on the confirmed call, once per token. A broken business rule reaches the AI client asfield: message. ShareChannelrecords whether a request came from the web, the API, or an AI client in Laravel'sContext.
Setup
composer setupand project creation generate Passport's keys, and a migration creates the personal access client.
For agents and maintainers
shipkit-core 5.0 adds checks and a rule for MCP tools (app/Mcp/Tools/{Feature}), the mcp rule, make:feature --mcp, and planning-skill prompts that ask whether a new feature should be reachable through an AI client. They pass on a project without tools.
Full details: CHANGELOG.md and shipkit-core's changelog.
ShipKit 3.0.0
Requires shipwithian/shipkit-core 3.0.
- Short frontend files: pages stay at or under 200 lines and other frontend files at or under 400, checked by
shipkit-core'sFrontendFileLengthTest, so a page composes partials instead of growing into one large file.
shipwithian/shipkit-core 3.0.0
A major release because a new check can fail a project that passed 2.x. UPGRADE.md has the fix.
- Checks:
FrontendFileLengthTestfails when a page inresources/js/pagesis over 200 lines or any other frontend file is over 400, so pages stay a list of partials instead of growing into thousand-line files. The vendoredcomponents/uiprimitives are exempt, andshipkit.architecture.long_frontend_filesrecords deliberate exceptions with their reasons. - Guidance: the
jsrule says how to split a file before it reaches the limit. - Internals:
ProjectLayout::frontendFiles()lists the project's own TypeScript sources, shared by both frontend checks.
Upgrading
Existing projects update the package with composer require shipwithian/shipkit-core:^3.0 and follow the 3.0 upgrade notes. Template changes are not applied to existing projects: compare the release tags and copy what you want.
ShipKit 2.0.0
Requires shipwithian/shipkit-core 2.0.
- Assign roles to users: a paginated Users page, gated by the new
assign rolessystem permission, sets the roles each user holds.SyncUserRolesActionrefuses to add or remove a role holding a permission the actor lacks, so nobody can grant themselvessuper-adminor strip it from someone with more access, and the last super admin still cannot lose the role.
shipwithian/shipkit-core 2.0.0
A major release because a new system permission makes a 1.0 project's seeder test fail. UPGRADE.md has the fix.
- Access Control records:
SystemPermission::AssignRoles(assign roles), the permission to put roles on users, reserved because nobody could otherwise change who holds which role. The bootstrap creates it protected and gives it tosuper-admin; existing projects runphp artisan db:seed --class=AccessControlSeederagain.
Upgrading
Existing projects update the package with composer require shipwithian/shipkit-core:^2.0 and follow the 2.0 upgrade notes. Template changes are not applied to existing projects: compare the release tags and copy what you want.
ShipKit 1.0.0
The first release that lets agents implement features unattended: the rules that protect the application's invariants are enforced by code, tests, and CI instead of by review, and they ship in a versioned package that updates with composer update.
There is no upgrade path from 0.x. ShipKit had no users before 1.0.0, so nothing in this release carries compatibility code. Create new projects from 1.0.0.
Enforcement moves into shipwithian/shipkit-core
- The guard traits, domain exception base class, architecture checks, guarded-table and cascade checks,
make:context, and the system roles and permissions live in the package, not in your project.composer updatebrings improved checks to a project that already exists. config/shipkit.phpmaps the project'sRole,Permission, andUsermodels. The application refuses to boot when a mapped model does not useGuardsInvariants.php artisan shipkit:verifyruns incomposer ci:checkand fails when core's testsuite is not registered inphpunit.xml, so deleting one line cannot silently turn the checks off.- The shared
.airules, skills, and guidelines come from the package through Boost (php artisan boost:update) instead of being copied into each project.
Guarded writes fail closed
- Every query-builder write on a guarded model throws, even for one row, and so do the inherited ones (
increment,decrement,touch,forceDelete,updateOrInsert, and the rest). Every Eloquent builder method is classified as guarded, rejected, or safe, and a test fails when a Laravel release adds one that is unclassified. is_protectedcannot change after a record is created, in either direction. One Access Control bootstrap Action, called by the seeder, is the only code that creates protected records, and a source check fails on any other file that writes the flag.- The last-super-admin guard is proven under real contention on MySQL and Postgres, in addition to the SQLite suite.
The frontend contract is generated from PHP
- Every Inertia page and the shared props are typed PHP classes.
composer types:generatewrites the TypeScript types toresources/js/types/generated.ts, andcomposer ci:checkfails when the committed file differs. This adds thespatie/laravel-typescript-transformerdependency. - Pages receive abilities computed with the Gate from the policy abilities their controllers authorize (
can.create, acanon each row,auth.canfor navigation). The UI no longer reads permission names,auth.permissionsis no longer shared, and a core check fails onpermissions.includes(...)inresources/js.
Agent workflow
.github/CODEOWNERSassigns the maintainer to the checks and to the places their exceptions are recorded. Theimplementandreview-diffskills state the evidence an agent needs to merge project-owned code: a regression test shown failing without the change, a greencomposer ci:check, and an independent review.docs/branch-protection.mdrecords the repository rules that enforce the split.- A CI job creates a project from the installer archive with the package from the same commit and runs
composer ci:checkin it, including the two cases that must fail.
Also since 0.1.0
- Code is organized into DDD-lite bounded contexts (
AccessControl,Settings,Authentication) withmake:contextto add more, enforced by architecture tests. - API token management endpoints for Sanctum.
- Agent skills are generated when a project is created, and the
verify-appskill drives an isolated copy of the app. league/commonmarkis updated for two security advisories.