feat(v5): gate V5 to development environments only

Introduce V5Feature and config so V5 routes, jobs, commands, morph maps,
and model queries run only when enabled. Move V5 migrations to
migrations-v5 (loaded only when enabled), remove Flux from production
Docker/install paths, and add isolation tests.
This commit is contained in:
Andras Bacsai committed 2026-07-19 11:44:12 +02:00
1 parent 1cb1d028de
commit d0247d3b09
45 files changed
+273 -218

No files matched your search

+4 -4
View File
@@ -3,6 +3,7 @@
namespace App\Console\Commands;
use App\Services\Flux\AgentTokenIssuer;
use App\Support\V5\V5Feature;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
@@ -13,8 +14,7 @@ class FluxDev extends Command
{host_id=coold-dev : Stable coold host id}
{--caps= : Comma-separated host capabilities}
{--ttl=3600 : Token lifetime in seconds}
{--output= : Optional path to write the token with 0600 permissions}
{--force : Allow running outside local/development environments}';
{--output= : Optional path to write the token with 0600 permissions}';
protected $description = 'Run Flux development helpers.';
@@ -30,8 +30,8 @@ class FluxDev extends Command
public function handle(AgentTokenIssuer $agentTokenIssuer): int
{
if (! app()->environment(['local', 'development', 'testing']) && ! $this->option('force')) {
$this->error('This command is intended for development only. Use --force to override.');
if (! V5Feature::enabled()) {
$this->error('V5 is only available in development environments.');
return self::FAILURE;
}
@@ -3,6 +3,7 @@
namespace App\Console\Commands;
use App\Services\Flux\AgentTokenIssuer;
use App\Support\V5\V5Feature;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
@@ -25,6 +26,12 @@ class V5FluxGenerateKeys extends Command
public function handle(AgentTokenIssuer $agentTokenIssuer): int
{
if (! V5Feature::enabled()) {
$this->error('V5 is only available in development environments.');
return self::FAILURE;
}
$privateKeyPath = (string) config('flux.jwt_private_key_path');
$publicKeyPath = (string) config('flux.jwt_public_key_path');
@@ -6,6 +6,7 @@ use App\Actions\V5\Server\SyncDevLimaServers;
use App\Models\PrivateKey;
use App\Models\Team;
use App\Models\User;
use App\Support\V5\V5Feature;
use Illuminate\Console\Command;
class V5SyncDevLimaServers extends Command
@@ -15,15 +16,14 @@ class V5SyncDevLimaServers extends Command
{--user-id=0 : User recorded as creator}
{--private-key-id= : Optional private key used by the dev servers}
{--cluster=Development-Lima : Cluster name for the dev Lima servers}
{--server=* : Server as name|host|ssh_user|ssh_port|wireguard_management_ip}
{--force : Allow running outside local/development environments}';
{--server=* : Server as name|host|ssh_user|ssh_port|wireguard_management_ip}';
protected $description = 'Sync development Lima VMs into the v5 server/cluster tables.';
public function handle(): int
{
if (! app()->environment(['local', 'development', 'testing']) && ! $this->option('force')) {
$this->error('This command is intended for development only. Use --force to override.');
if (! V5Feature::enabled()) {
$this->error('V5 is only available in development environments.');
return self::FAILURE;
}
+5 -7
View File
@@ -18,6 +18,7 @@ use App\Jobs\UpdateCoolifyJob;
use App\Jobs\V5ReconcileServersJob;
use App\Jobs\V5RotateAgentTokensJob;
use App\Models\InstanceSettings;
use App\Support\V5\V5Feature;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -51,13 +52,10 @@ class Kernel extends ConsoleKernel
$this->scheduleInstance->command('sanctum:prune-expired --hours=1')->hourly()->onOneServer();
$this->scheduleInstance->job(new ApiTokenExpirationWarningJob)->hourly()->onOneServer();
// V5 reconciliation loop: pull-based safety net for the push-only
// coold -> flux -> webhook status pipeline, plus container status pruning.
$this->scheduleInstance->job(new V5ReconcileServersJob)->everyFiveMinutes()->withoutOverlapping()->onOneServer();
// V5 host JWT rotation: re-mints and SSH-pushes a fresh host token
// before the on-disk token expires so coold reconnects stay authorized.
$this->scheduleInstance->job(new V5RotateAgentTokensJob)->everyFifteenMinutes()->withoutOverlapping()->onOneServer();
if (V5Feature::enabled()) {
$this->scheduleInstance->job(new V5ReconcileServersJob)->everyFiveMinutes()->withoutOverlapping()->onOneServer();
$this->scheduleInstance->job(new V5RotateAgentTokensJob)->everyFifteenMinutes()->withoutOverlapping()->onOneServer();
}
if (isDev()) {
// Instance Jobs