Skip to content

Releases: teylabs/concierge

v0.9.3

Choose a tag to compare

@jaspertey jaspertey released this 30 Oct 13:27
9662ea1

Added

  • MembersIndex::scopeMembersUsing($scope): a host narrows the members the index lists and counts with its own rule (a function (Builder $memberships, Workspace $workspace): void). It's applied to both the rows and the Members tab count, inside a nested where, so the host's orWhere can't widen the workspace, guest or status constraints. Without a scope, the index behaves as before.

What's Changed

Full Changelog: v0.9.2...v0.9.3

v0.10.0

Choose a tag to compare

@jaspertey jaspertey released this 29 Oct 12:44
9271572

Added

  • php artisan concierge:install detects the app, asks only what it can't detect, and sets concierge up, printing one line per step (#144).
    • It detects the starter kit (Vue, React, Svelte, Livewire or none), WorkOS AuthKit, the sign-in, the user model, any team or organisation model, and Spatie's setup.
    • It asks whether to adopt a team model, which mode to use, and whether to turn on local login, each with a flag; --no-interaction takes the defaults.
    • It publishes and configures Spatie permission, writes config/concierge.php, migrates and syncs roles.
    • It never rewrites models: one missing a contract gets the exact lines to add, and the command stops until you run it again.
    • Running it again changes nothing that's already in place. --dry-run --json gives the plan.
  • Default routes, controllers and policies, each overridable (#145). Named concierge.* routes cover managing members (index, invite, member page, roles, suspend, reinstate, remove, transfer ownership, invitation resend and revoke) and accepting invitations.
    • They're registered only when concierge is set up, membership is on and concierge.routes.enabled is true. concierge:install writes 'enabled' => true; the package default is false.
    • Override pieces through config (routes.prefix, routes.middleware, routes.invitation_middleware, routes.home, routes.pages), by binding a controller subclass, or through Gate abilities (Tey\Concierge\Http\Abilities). An app's own ability definition always wins.
    • Pages render through the new Contracts\PageResponder: Inertia when installed, JSON otherwise.
    • There are new page-prop builders (MemberPage, InviteMemberPage, AcceptInvitationPage, RoleOptions), plus MembersIndex::row() and roleBadges().
    • With the routes on, Concierge::invitationUrl() defaults to concierge.invitations.show; invitationUrlUsing() still wins.
  • Laravel Boost guidance (#146): a guideline and a concierge-development skill, so coding agents use concierge's actions and routes instead of rebuilding them. A test fails if a command, option, action or event ships without being named in the skill.
  • Read-only Boost MCP tools concierge-status and concierge-plan (#147). They're added to Boost's MCP server when Boost is installed, and they work before setup. Inviting, granting roles and creating owners stay with php artisan concierge:*.

Changed

  • Concierge registers nothing until it's set up (#142). Without the app's own config/concierge.php (or with 'setup' => false), it registers no migrations, routes, middleware, listeners, bindings or commands other than concierge:install. So composer require tey/concierge followed by php artisan migrate creates no concierge tables. php artisan about shows the setup state.

Fixed

  • The scaffolding tests are safe under pest --parallel: each test process gets its own copy of the Testbench skeleton (#148).

Upgrading

  • Existing apps: nothing changes. Your config/concierge.php marks concierge as set up, so it registers exactly as before. Without a routes key, the new routes stay off, no Gate abilities are defined, and invitation links still come only from invitationUrlUsing().
  • To adopt the default routes, copy the routes block from the package config into config/concierge.php with 'enabled' => true, run php artisan concierge:pages if you haven't, and remove your own routes and controllers for the same pages. Check that routes.prefix doesn't clash with your paths; the default is no prefix, giving /members and /invitations/{token}. Add rate limiting through routes.invitation_middleware (for example throttle:30,1).
  • With Laravel Boost, run php artisan boost:install or boost:update to pick up the guidance and tools.
  • Running concierge:install on an app that's already set up is safe: it reports what's in place and changes nothing.

What's Changed

  • Register nothing until concierge is set up by @jaspertey in #142
  • Add concierge:install to detect the app and set concierge up by @jaspertey in #144
  • Ship routes, controllers and policies by default, each overridable by @jaspertey in #145
  • Add Laravel Boost guidelines and a concierge-development skill by @jaspertey in #146
  • Add read-only concierge-status and concierge-plan tools to Laravel Boost by @jaspertey in #147
  • Make the scaffolding tests safe under pest --parallel by @jaspertey in #148
  • Release 0.10.0 by @jaspertey in #151

Full Changelog: v0.9.2...v0.10.0

v0.9.2

Choose a tag to compare

@jaspertey jaspertey released this 29 Oct 08:34
1ebe0c5

Fixed

  • A comma in the members or invitations search no longer returns a 500. MembersIndex registered its search and role filters with Spatie's default , delimiter and then cast the value with (string), so ?filter[search]=Viewer, Owner hit "Array to string conversion". Search and role now take one value (no delimiter). An array value (filter[search][]=x, sort[]=x, tab[]=x) is ignored as invalid input, or for tab refused as an unknown tab, and never cast to a string. This matches tey/kit 0.5.21's TableQuery fix.

What's Changed

Full Changelog: v0.9.1...v0.9.2

v0.9.1

Choose a tag to compare

@jaspertey jaspertey released this 29 Oct 03:36
d6fd974

Changed

  • The current workspace and a user's memberships are read once per request and remembered in a request-scoped RequestMemo (bound with scoped(), so it's dropped between requests, queued jobs and Octane requests; no cache store). It's cleared by writes to the membership, ownership, workspace, user and role tables, by a transaction rollback, and when the workspace context is entered or left. For 5 repeated calls in one request: member() 20 → 4 queries, workspacesOf() 25 → 5, current() 5 → 1 (#141, closes #128).

Known limitations

  • A host that writes those tables over another database connection, or with query events off, sees the remembered reads until the request ends.

What's Changed

Full Changelog: v0.9.0...v0.9.1

v0.9.0

Choose a tag to compare

@jaspertey jaspertey released this 28 Oct 23:27
bc18247

Added

  • Scoped invitations: invite someone to act on one record with no other access in the workspace. An invitation takes an optional scope (a morph type and id, part of its fingerprint). Accepting it creates a guest-only membership plus a concierge_membership_scopes row (invited_by, accepted_at, revoked_at). A full member given a scope stays a full member and gains the scope (#138, closes issue 133).
  • ScopedAccess contract with a membership-backed implementation, a concierge.scoped:{param}[,members] middleware, and facade helpers, for routes, policies and MCP tools to share one access check.
  • RevokeScope: immediate. It kills a pending link, and removes the guest membership when it was the last scope; the account survives. ScopeAccepted and ScopeRevoked events.
  • Guest exclusions by default: scoped guests are left out of member lists, role pickers, role changes, ownership transfer and concierge.member (a guest-only membership gets 403).

Changed

  • Accepting a scoped invitation whose record was deleted refuses with scope_missing, writes nothing and leaves the invitation pending. A swapped record is refused as GrantsChanged through the fingerprint.

Upgrading

  • Run the new migration (update_concierge_invitations_with_scope). App mode refuses a scope.

Known limitations

  • No invite UI, concierge:invite option or TypeScript types for scopes yet; those follow.

What's Changed

Full Changelog: v0.8.4...v0.9.0

v0.8.4

Choose a tag to compare

@jaspertey jaspertey released this 28 Oct 20:03
c4c5df2

Added

  • WorkspaceContext::enter($workspace, $refresh) and leave(), an explicit enter/leave alongside the scoped run(). Entries nest, and leave() with nothing entered throws LogicException. run() is now enter, try, finally leave, with unchanged behaviour. This lets a tenant switch task (spatie/laravel-multitenancy) hold the workspace context across a job or command (#136).

Upgrading

  • An app with its own WorkspaceContext implementation must add enter() and leave().

What's Changed

Full Changelog: v0.8.3...v0.8.4

v0.8.3

Choose a tag to compare

@jaspertey jaspertey released this 28 Oct 14:51
dbaecbd

Fixed

  • An invited sign-up through Fortify's registration is created with its email verified when a remembered pending invitation (member or tenant) matches the address, so the invitee gets one email, not a second verification email.
  • Accepting an invitation as a signed-in, unverified account whose email is the invited address verifies it inside the accept transaction (member, app-wide and tenant invitations) and reports it Ready. A different address is still Unverified.
  • A successful password reset through the emailed signed link verifies the account, since it proves control of the mailbox. This covers the first owner created by concierge:owners. See README, Security (#134).

What's Changed

Full Changelog: v0.8.2...v0.8.3

v0.8.2

Choose a tag to compare

@jaspertey jaspertey released this 25 Oct 14:54
3d81174

Changed

  • Allows tey/mod 0.11, which adds removing and moving generated files on update and fixes discovery stopping production boots without dev dependencies (#130).

Full Changelog: v0.8.1...v0.8.2

v0.8.1

Choose a tag to compare

@jaspertey jaspertey released this 21 Oct 17:30
37656e9

Added

  • Join as the signed-in account. A new concierge.invitations.match_email setting (default true, today's behaviour). When it's false, someone signed in with a different, verified email can accept an invitation as themselves. Every other check still applies: expiry, revoked, the workspace accepting members, the inviter still authorised, grants unchanged, and already a member. The invitation records who accepted it and whether they used a different email (#127).
  • The accept page offers "Join as {email}" before Switch account, with "This invitation was sent to {email}." Member rows in MembersIndex carry acceptedWithOtherEmail and invitedEmail, so a host can show who accepted.
  • docs/scenarios.md adds the join-as cases and updates the accept decision tree.

Upgrade notes

  • Publish and run the new migration (update_concierge_invitations_with_acceptor). It adds one boolean column, defaulting to false.
  • Nothing else changes unless you set concierge.invitations.match_email to false. To offer Join as, pass otherEmail: $inspection->otherEmail, signedInAs and the switchAccount link on the ready state.

Full Changelog: v0.8.0...v0.8.1

v0.8.0

Choose a tag to compare

@jaspertey jaspertey released this 21 Oct 13:23
76510f2

Added

  • Switch account on invitations. Someone signed in as a different email who opens an invitation sees who it is for, with Switch account and Cancel. Switching keeps a signed, short-lived invitation token through sign-out and sign-in (or registration) and returns them to accept it. A signed-out person with an existing account signs in and comes straight back to accept.
  • No-workspace landing. A member who is removed, leaves or is suspended is moved to their next active workspace, or to none, and sent to workspaces.no_workspace_url instead of an error.
  • CopyInvitationLink issues a new invitation link without sending an email, and the previous link stops working. MembersIndex offers it as a copy_link row action to people who may resend, with the confirmation "The previous link will stop working."
  • Invitations take an optional personal message (up to 500 characters), set by InviteMember and ResendInvitation. It's shown on the invitation, on member-list rows and in the invitation email.
  • Suspend and reinstate row actions ask for confirmation.
  • docs/scenarios.md: every membership and invitation scenario with the test that proves it, plus diagrams of accepting an invitation, the membership lifecycle and where a member lands after signing in.

Fixed

  • Searching members and invitations ignores case on every database. It was case-sensitive on PostgreSQL.

Upgrade notes

  • Publish and run the new migration (update_concierge_invitations_with_message).
  • Member-only workspace routes now return 404 to someone who was never a member (it was 403). A past member is redirected to workspaces.no_workspace_url instead.
  • The accept.wrong_account label has new wording. accept.wrong_account_as, accept.switchAccount and accept.cancel are new labels.
  • MembersIndex rows include a copy_link action when the host maps invitation.copyLink.

Full Changelog: v0.7.6...v0.8.0