Add a SHA-256 token_hash lookup column to workspace_invitations so that
findByToken and findPendingByToken can locate the candidate row with a
single indexed SQL query instead of loading up to 1000 rows and running
bcrypt against each one sequentially.
The bcrypt hash in the token column is still verified after the O(1)
lookup, preserving the existing security guarantee while eliminating
both the timing side-channel and the performance bottleneck.
Changes:
- Migration to add nullable indexed token_hash column
- Model booted() creating/updating events compute SHA-256 alongside bcrypt
- findByToken/findPendingByToken rewritten to WHERE token_hash then Hash::check
- HashInvitationTokens command updated to populate token_hash for existing rows
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added declare(strict_types=1) to 27 files that were missing it.
Ran Pint to fix PSR-12 issues (import ordering, operator spacing, brace
positioning) across 33 files.
Co-Authored-By: Virgil <virgil@lethean.io>
Aligns composer package name with forge repo path
(forge.lthn.ai/core/php-tenant). Part of host-uk/* → core/* migration.
Co-Authored-By: Virgil <virgil@lethean.io>
Replace github.server_url/GITHUB_REF_NAME with explicit forge URL
and GITEA_REF_NAME/GITEA_OUTPUT.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On tag push (v*), zips the package and publishes to the
forge.lthn.ai Composer package registry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace setup-php action with pre-built container.
Eliminates ~50s setup overhead per matrix job.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Direct git clone of ../php-framework avoids shell escaping
issues with dynamic PHP-based path extraction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch php -r argument to single quotes so PHP dollar signs
are not interpreted by bash. Pipe output to while-read loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The PHP variables inside php -r need \$ escaping, but shell
variables outside need bare $ for command substitution and
variable expansion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Forgejo act runner caches reusable workflow definitions,
preventing updates from being picked up. Inline the workflow
with dependency checkout step.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The reusable php-test.yml now detects pest/phpunit/pint availability
and clones path dependencies using the runner token.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add phpunit.xml and tests/Pest.php for standalone test execution.
Apply Laravel Pint formatting fixes across all source files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automated scan of all PHP source files, migrations, routes, tests, and
documentation. Created 34 individual issues and 1 roadmap tracking issue
(#5-#38) on forge.lthn.ai covering security, bugs, performance, tests,
refactors, and features.
Closes#3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add comprehensive Pest tests for namespace-level entitlements including:
- canForNamespace() with user-owned and workspace-owned namespaces
- Entitlement cascade (namespace -> workspace -> user tier)
- provisionNamespacePackage() with replacement, expiry, metadata
- provisionNamespaceBoost() with stacking and unlimited overrides
- recordNamespaceUsage() with metadata and workspace context
- getNamespaceUsageSummary() with percentages and near-limit detection
- invalidateNamespaceCache() for limits and usage
- Multiple namespaces with separate usage tracking
- Boost stacking behaviour
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Document provisionNamespacePackage with examples
- Document provisionNamespaceBoost with boost/duration types
- Document invalidateNamespaceCache with auto-invalidation triggers
- Add cross-references to workspace-level equivalents
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add declare(strict_types=1) to Workspace.php
- Add declare(strict_types=1) to User.php
- Add declare(strict_types=1) to EntitlementService.php
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
P1-010: Rate limiting (60 req/min) on EntitlementApiController
P1-011: API authentication documentation and middleware
P1-014: SSRF protection for webhook endpoints (PreventsSSRF trait)
P1-015: Workspace access validation in middleware (breaking change)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add encrypted cast to UserTwoFactorAuth secret and recovery_codes
- Hash invitation tokens on creation using Hash::make()
- Update token verification to use Hash::check()
- Add migration commands for existing data:
- security:encrypt-2fa-secrets
- security:hash-invitation-tokens
- Add tests for encryption and hashing
Fixes SEC-003, SEC-004 from security audit.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix namespace from Core\Mod\Tenant to Core\Tenant, add workspace
isolation patterns, coding standards, and entitlement system overview.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Simplifies the namespace hierarchy by removing the intermediate Mod
segment. Updates all 118 files including models, services, controllers,
middleware, tests, and composer.json autoload configuration.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TeamManager Livewire component for managing workspace teams
- Add MemberManager Livewire component for managing workspace members
- Add admin routes for team and member management
- Add blade templates for team and member management UI
- Support team permissions, bulk operations, and custom member permissions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>