Skip to content

Releases

ShipKit 5.1.0

Choose a tag to compare

@ianlabicani ianlabicani released this 11 Nov 06:59

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:use scope 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_profile is 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): UpdateProfileAction takes the Channel and logs it. ShareChannel shares 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 with Gate::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} --mcp works on a feature that already exists, and adds the tool path and glob to its rule file and rules index.
  • ChannelKind replaces the Channel::WEB, API, and MCP constants.

Full details: CHANGELOG.md and shipkit-core's changelog.

ShipKit 5.0.0

Choose a tag to compare

@ianlabicani ianlabicani released this 11 Nov 06:58

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/v1 endpoints keep their requests and responses, but issue Passport personal access tokens (longer, signed tokens), require the api scope on every authenticated route, and identify tokens by a string id.
  • PERSONAL_ACCESS_TOKEN_LIFETIME_DAYS (90 by default) replaces SANCTUM_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:use scope, so they never reach /api/v1), and connect through the consent screen.
  • The first tools are show_my_profile, update_my_profile, and list_roles. Tools act as the signed-in user, through the same policies, Actions, and Queries as the web app. Personal access tokens work on /mcp too.
  • 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 as field: message.
  • ShareChannel records whether a request came from the web, the API, or an AI client in Laravel's Context.

Setup

  • composer setup and 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

Choose a tag to compare

@ianlabicani ianlabicani released this 06 Nov 00:49

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's FrontendFileLengthTest, 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: FrontendFileLengthTest fails when a page in resources/js/pages is 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 vendored components/ui primitives are exempt, and shipkit.architecture.long_frontend_files records deliberate exceptions with their reasons.
  • Guidance: the js rule 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

Choose a tag to compare

@ianlabicani ianlabicani released this 06 Nov 00:48

Requires shipwithian/shipkit-core 2.0.

  • Assign roles to users: a paginated Users page, gated by the new assign roles system permission, sets the roles each user holds. SyncUserRolesAction refuses to add or remove a role holding a permission the actor lacks, so nobody can grant themselves super-admin or 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 to super-admin; existing projects run php artisan db:seed --class=AccessControlSeeder again.

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

Choose a tag to compare

@ianlabicani ianlabicani released this 27 Oct 03:27

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 update brings improved checks to a project that already exists.
  • config/shipkit.php maps the project's Role, Permission, and User models. The application refuses to boot when a mapped model does not use GuardsInvariants.
  • php artisan shipkit:verify runs in composer ci:check and fails when core's testsuite is not registered in phpunit.xml, so deleting one line cannot silently turn the checks off.
  • The shared .ai rules, 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_protected cannot 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:generate writes the TypeScript types to resources/js/types/generated.ts, and composer ci:check fails when the committed file differs. This adds the spatie/laravel-typescript-transformer dependency.
  • Pages receive abilities computed with the Gate from the policy abilities their controllers authorize (can.create, a can on each row, auth.can for navigation). The UI no longer reads permission names, auth.permissions is no longer shared, and a core check fails on permissions.includes(...) in resources/js.

Agent workflow

  • .github/CODEOWNERS assigns the maintainer to the checks and to the places their exceptions are recorded. The implement and review-diff skills state the evidence an agent needs to merge project-owned code: a regression test shown failing without the change, a green composer ci:check, and an independent review. docs/branch-protection.md records 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:check in it, including the two cases that must fail.

Also since 0.1.0

  • Code is organized into DDD-lite bounded contexts (AccessControl, Settings, Authentication) with make:context to add more, enforced by architecture tests.
  • API token management endpoints for Sanctum.
  • Agent skills are generated when a project is created, and the verify-app skill drives an isolated copy of the app.
  • league/commonmark is updated for two security advisories.