@php $admin = auth('admin')->user(); $inSync = $server->applied_state_hash === $desiredHash; @endphp
{{-- ============================================================ Header --}}

{{ $server->name }}

{{ $server->status->label() }}

{{ $server->endpoint() }}

{{ $server->location?->display_name }} · {{ $server->provider ?: 'Unknown provider' }}

@if ($admin->hasPermission('vpn_servers.update')) Edit @foreach (\App\Enums\ServerStatus::adminAssignable() as $status) @continue($status === $server->status)
@csrf @method('PATCH') Mark {{ $status->label() }}
@endforeach @endif
{{-- ============================================================= Stats --}} @php $stats = [ ['Peers', $activePeers.' / '.$server->capacity, 'active / capacity'], ['Free addresses', number_format($freeAddresses), 'across active pools'], ['Last heartbeat', $server->last_heartbeat_at?->diffForHumans() ?? 'never', $server->isHeartbeatFresh() ? 'healthy' : 'stale'], ['CPU / RAM', ($server->cpu_usage !== null ? round($server->cpu_usage).'%' : '—').' / ' .($server->ram_usage !== null ? round($server->ram_usage).'%' : '—'), 'latest sample'], ]; @endphp
@foreach ($stats as [$label, $value, $meta])

{{ $label }}

{{ $value }}

{{ $meta }}

@endforeach
{{-- ========================================================= Node sync --}}
Sync state
{{ $inSync ? 'In sync' : 'Pending sync' }}
Desired state hash
{{ $desiredHash }}
Applied state hash
{{ $server->applied_state_hash ?? 'never reported' }}
Server public key
{{ $server->public_key ?? 'awaiting first heartbeat' }}
@unless ($inSync)

The agent picks changes up on its next poll (every {{ config('zylovpn.node.poll_interval') }}s). A hash that stays stale means the agent is not running or cannot reach this panel.

@endunless @if ($admin->hasPermission('vpn_servers.rotate_token'))

Agent token

Only its hash is stored. Rotating invalidates the current token immediately — the node stops syncing until you install the new one. {{ $server->agent_token_rotated_at ? 'Last rotated '.$server->agent_token_rotated_at->diffForHumans().'.' : '' }}

@csrf Rotate token
@endif
{{-- ========================================================== IP pools --}} @if ($admin->hasPermission('ip_management.manage')) Add pool @endif @if ($server->ipPools->isEmpty()) @if ($admin->hasPermission('ip_management.manage')) Add pool @endif @else
    @foreach ($server->ipPools as $pool)
  • {{ $pool->cidr() }}

    gateway {{ $pool->gateway }} · {{ $pool->assigned_addresses }}/{{ $pool->total_addresses }} assigned

    @php $pct = min(100, (int) round($pool->utilisation() * 100)); @endphp
    {{ $pool->is_active ? 'Active' : 'Draining' }} @if ($admin->hasPermission('ip_management.manage'))
    @csrf @method('PATCH') {{ $pool->is_active ? 'Disable' : 'Enable' }}
    @endif
  • @endforeach
@endif
{{-- ==================================================== Danger zone --}} @if ($admin->hasPermission('vpn_servers.delete'))

Removal is blocked while peers remain on this node. Put it in maintenance first and let its peers rotate elsewhere.

@csrf @method('DELETE') Remove server
@endif