Skip to content

fix: bootstrap Passport personal access client - #206

Closed
drbelt27 wants to merge 1 commit into
trypostit:mainfrom
drbelt27:fix/passport-personal-access-bootstrap
Closed

fix: bootstrap Passport personal access client#206
drbelt27 wants to merge 1 commit into
trypostit:mainfrom
drbelt27:fix/passport-personal-access-bootstrap

Conversation

@drbelt27

Copy link
Copy Markdown

Summary

This change bootstraps the Laravel Passport Personal Access Client required for API key creation in production Docker deployments.

It adds a focused Artisan command, trypost:bootstrap-passport, and invokes it from the production entrypoint after database migrations and Passport key initialization.

Problem

A fresh production deployment can have:

  • all Passport migrations applied;
  • Passport public and private keys present;
  • all OAuth tables available;

while still having no Personal Access Client in oauth_clients.

When a user creates an API key from the workspace settings, TryPost calls User::createToken(...). Laravel Passport then fails with HTTP 500:

Personal access client not found for 'users' user provider. Please create one.

Reproduction

  1. Start with a fresh production database.
  2. Run the database migrations.
  3. ensure Passport keys are available.
  4. Do not execute PassportSeeder.
  5. Open the workspace API key settings.
  6. Attempt to create an API key.
  7. Observe the HTTP 500 exception indicating that the Personal Access Client is missing.

Root cause

TryPost already contains Database\Seeders\PassportSeeder, and the main DatabaseSeeder includes it.

However, the production Docker entrypoint runs migrations and prepares Passport keys without running the focused Passport seeder.

As a result, the application can start successfully while User::createToken(...) remains unusable because no active Personal Access Client exists.

Solution

This pull request:

  • adds the trypost:bootstrap-passport Artisan command;
  • invokes only Database\Seeders\PassportSeeder;
  • runs the command during production container startup after migrations and Passport key preparation;
  • does not run the full DatabaseSeeder;
  • does not regenerate Passport keys;
  • does not hide bootstrap failures behind || true;
  • exits with a non-zero status and a clear message if the bootstrap fails.

Idempotency and concurrency

The bootstrap is designed to be safe across repeated and concurrent production starts.

It:

  • reuses an existing active Personal Access Client;

  • supports valid clients with either a null provider or the users provider;

  • does not create duplicates during repeated starts;

  • does not revoke or replace valid clients;

  • does not invalidate existing access tokens;

  • creates a new valid client when only revoked clients exist;

  • serializes concurrent bootstrap attempts using:

    • PostgreSQL advisory locks;
    • MySQL/MariaDB GET_LOCK.

Locks are released after execution, including failure paths.

Testing

Full test suite

2744 passed
21374 assertions
3 skipped
Duration: 1886.01s

The full suite was executed against a dedicated local PostgreSQL test database. No production database or production credentials were used.

Focused bootstrap tests

php artisan test tests/Feature/PassportBootstrapCommandTest.php --compact

10 passed
34 assertions
Duration: 47.83s

Related API key tests

PassportBootstrapCommandTest + ApiKeyControllerTest

19 passed
59 assertions
Duration: 57.46s
PassportBootstrapCommandTest + Api/ApiKeyApiTest

16 passed
54 assertions
1 skipped
Duration: 43.02s
PassportBootstrapCommandTest + Mcp/ApiKeyToolTest

19 passed
76 assertions
Duration: 53.65s

Additional checks

  • focused Pint check: passed;
  • git diff --check: passed;
  • bash -n docker/entrypoint.sh: passed;
  • ShellCheck was not available in the local environment.

The local test environment used a dedicated PostgreSQL database named trypost_test and isolated Passport test keys. No production environment was involved.

Backward compatibility

Existing installations with a valid Personal Access Client remain unchanged.

This change does not:

  • regenerate Passport keys;
  • revoke Personal Access Clients;
  • replace valid clients;
  • invalidate existing access tokens;
  • run the full application seeder;
  • change the public API behavior beyond ensuring the required Passport client exists.

Scope

This pull request only addresses Passport Personal Access Client bootstrap during production Docker startup.

It does not include:

  • MCP Asset Library changes;
  • Reverb or frontend configuration changes;
  • deployment infrastructure specific to any installation;
  • unrelated refactors;
  • backup, monitoring, SMTP, or reverse proxy configuration.

Risk

The risk is low and limited to production startup.

The entrypoint now fails explicitly when Passport bootstrap cannot complete. This is preferable to allowing the application to start successfully while API key creation remains broken with an HTTP 500 error.

Rollback

Rollback consists of reverting this commit.

Any Personal Access Client already created remains valid, and existing clients and tokens are not modified by the rollback.

Reviewer notes

The main review points are:

  • the focused trypost:bootstrap-passport Artisan command;
  • its position in the production entrypoint after migrations and Passport key preparation;
  • the idempotency behavior;
  • database locking for concurrent starts;
  • preservation of existing clients and tokens;
  • regression coverage for API key creation.

@paulocastellano

paulocastellano commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the thorough write-up and the focused tests — the underlying gap is real (fresh prod can have Passport keys + migrations without a Personal Access Client).

We're going to close this PR though. A dedicated trypost:bootstrap-passport command + advisory locks + entrypoint wiring is more machinery than we want for a one-time install concern.

What we already have:

  • Database\Seeders\PassportSeeder (idempotent) — creates the Personal Access Client
  • php artisan passport:client --personal — the upstream equivalent
  • Self-host docs already point at db:seed, which runs that seeder

We've updated the self-hosting docs instead of adding runtime bootstrap. Passport setup is two one-time install steps:

  1. php artisan passport:keys — RSA key files (filesystem / env)
  2. php artisan db:seed — Personal Access Client (database)

Docs (live):

Those are separate concerns; regenerating keys on every boot (or from a seeder) would invalidate existing tokens, so we intentionally keep them as documented one-time install steps rather than runtime bootstrap code.

Appreciate the report — docs were the right fix here.

@paulocastellano

Copy link
Copy Markdown
Contributor

Closing in favor of documenting the one-time Passport install steps (keys + Personal Access Client) instead of adding runtime bootstrap.

@paulocastellano

Copy link
Copy Markdown
Contributor

Follow-up: the self-hosting docs are live with the Passport + install steps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants