Platform Control Infrastructure

Control station: configuration and governance that shape what operators see on the live workspace after refresh or the next poll.

Linked workspace: Operations dashboard, Task queue, and Accounts.

@if (session('status'))

{{ session('status') }}

@endif @if ($errors->any())
@foreach ($errors->all() as $err)

{{ $err }}

@endforeach
@endif
Control panel — full capability list

Access & scope

  • URLs: overview GET /control/panel (route('control.panel')); other sections under /control/panel/<section> (see routes/web.php).
  • Authorization: a user with the admin role (resolved as the platform administrator for this panel) is required. If none exists, set CONTROL_PANEL_ACCESS_TOKEN in .env and open /control/panel?access_token=… (or send header X-Control-Panel-Token) once per browser session; in local with an empty env value, the fallback token in config/control_panel.php applies. Otherwise the page returns 403.
  • Operator surfaces: GET /dashboard (route('dashboard')) Operations Intelligence Center; GET /operations/live (route('operations.live')) JSON poll; GET /accounts/live (route('accounts.live')) accounts snapshot JSON.

Operations, voyages & treasury exposure

  • Scheduler: app:tick-voyages (see routes/console.php) runs the voyage engine and, when operations_enabled is on, the operational circulation cycle (queues, spawns, archives).
  • Flags: operations_enabled gates circulation; voyage_generation_enabled allows voyage ticks inside that cycle; voyage_operational_invoice_staging (default off) lets the engine stage SGS/charter/port invoices into payables.
  • Settlement staging: voyage payment_progress follows 0→50%→80% of journey time, then an 80→100% tail on wall-clock hours (config('operations.payment_tail_completion_hours'), env VOYAGE_PAYMENT_TAIL_COMPLETION_HOURS). Paid/outstanding on Operations cards aggregate cargo_value × payment_progress across voyages.
  • Activity feed: crossing 50%, 80%, and completion clips emits treasury_payment_milestone in-app messages (activity rail “Treasury settlement”).

Accounts, company treasury headline & mandate earnings

  • Treasury summary: built from ledger throughput, linked reserve, and live voyage exposure (same engine as Operations). Exposure “paid” rolls into that headline.
  • Mandate participation: config('accounts.mandate_treasury_participation_rate') / env MANDATE_TREASURY_PARTICIPATION_RATE (default 3%). MandateEarningsCreditProtocolService::accrueTreasuryHeadlineGrowth credits available_balance by the rate × increase in the company treasury headline after the row is anchored (treasury_headline_accrual_waterline); opening baseline config('accounts.mandate_opening_withdrawable_usd'). Treasury dips do not claw back.
  • Working registry: operator route GET /accounts/working-registry (route('accounts.working-registry')) — bank-side registration flows; gated OTP via working_account_inclusion_otp.

Task queue directives (route('control.panel.task-queue-directives'))

  • POST /control/panel/task-queue-messages — create draft or publish; publish fans out cp_task_queue_message in-app notifications. POST …/{message}/publish and …/archive for lifecycle.

Task queue issuance hub & templates (route('control.panel.invoice-issuance'), route('control.panel.invoice-templates'))

  • Pipeline totals — the hub shows open (non-paid, non-disputed) invoice counts and USD sums per domain; combined total is the headline exposure before settlement.
  • Create templatePOST /control/panel/invoice-templates: title, category, issuer, optional country (ISO-2), amount-range label, payment reference pattern, invoice_domain (operational_payable or earnings_payable), optional JSON account details.
  • Issue invoice from templatePOST /control/panel/invoice-templates/{template}/issue: amount, optional voyage id, optional line_items_json (array of {label, amount} that must sum to amount). Gated by invoice_generation_enabled. Creates a received invoice (Task queue + Payables) and notifies the acting admin.

Outbound security (wires & invoice-related outbound)

  • Resolve security flagPOST /control/panel/security-flags/{securityFlag}/resolve with optional resolution_note. Closes the hold row; when no open VPN/manual outbound flags remain, lifts durable outbound suspension; when no open location-corridor flags remain, restores active from restricted_payout_access.
  • Trigger governance noticePOST /control/panel/governance/trigger-notice with user_id, subject, message (in-app + email, type control_panel_governance_notice). Optional: attach_invoice=1, invoice_domain, invoice_amount, invoice_category, invoice_attachment (PDF) — adds a Payables deep link and Process invoice CTA.
  • Enable outbound blockPOST /control/panel/outbound-security/apply with user_id and optional note. Sets the durable outbound_security_suspended flag (same effect as automated VPN wire detection): blocks Secure wire submission, international payouts, transaction initiation, operational debits, mandate earnings invoice pay, and operational payment-request submission until cleared.
  • Granular workspace capability blocksPOST /control/panel/workspace-capability-blocks with user_id and optional checkboxes cp_block_wire_transfer, cp_block_invoice_activity, cp_block_withdraw_earnings, cp_block_linked_reserve_topup (omit or unchecked = off). UI: Outbound security (second table).
  • Block working accountPOST /control/panel/accounts/{account}/block sets status to suspended (treasury-wide core ledgers without an owner stay protected). RestorePOST /control/panel/accounts/{account}/unblock sets active. UI: Working account blocks.
  • Restore outbound accessPOST /control/panel/outbound-security/clear with user_id: clears the suspension and resolves open vpn_proxy and manual_outbound_hold security flags for that user.
  • You cannot apply a hold to your own account from the panel (prevents accidental self-lockout).

Treasury-linked cards (route('control.panel.treasury-cards'))

  • IssuePOST /control/panel/cards/issue (route('control.panel.cards.issue')): user_id, optional cardholder_name. Gated by card_issuance_enabled.
  • Freeze / UnfreezePOST .../cards/{card}/freeze and POST .../cards/{card}/unfreeze. Sends in-app notice to the cardholder referencing the Control Panel.
  • DisbandPOST .../cards/{card}/disband with optional note (shown to the cardholder). Ends the instrument (terminated); notifies the cardholder. Deep-link operators back here via \App\Support\WorkspaceNavigation::controlPanelTreasuryCards().
  • Security hold / clearPOST .../security-hold and POST .../security-clear.
  • TerminatePOST .../cards/{card}/terminate (route('control.panel.cards.terminate')) programmatic termination with the standard notice.
  • Simulate authorizationPOST .../simulate-transaction (route('control.panel.cards.simulate-transaction', $card)).
  • Operator card UI: \App\Support\WorkspaceNavigation::cardManagement()route('cards.index'); related routes under /cards/* (transactions, spending controls, security events).

Feature toggles (global flags)

Each row toggles POST /control/panel/toggle with key and enabled (boolean). Keys aligned with FeatureFlagSeeder (plus any you add in feature_flags):

  • payouts_enabled
  • invoices_enabled
  • operations_enabled
  • settlements_enabled
  • voyage_generation_enabled
  • transfer_routing_enabled
  • notifications_enabled
  • vpn_enforcement_enabled
  • payout_restrictions_enabled
  • location_validation_enabled
  • earnings_invoice_payments_enabled
  • invoice_auto_approval_operational
  • invoice_generation_enabled
  • settlement_automation_enabled
  • card_management_enabled
  • card_issuance_enabled
  • card_international_routing_enabled
  • card_atm_enabled
  • card_online_payments_enabled
  • card_contactless_enabled
  • card_replacement_enabled
  • magic_login_enabled (portal one-time login links)
  • working_account_inclusion_otp (default off)
  • voyage_operational_invoice_staging (default off)

Inline descriptions on this page cover several invoice-, OTP-, card-, and voyage-staging flags; remaining keys are platform-wide switches in feature_flags. Add a short hint block below when you introduce a new key.

@foreach (config('control_panel.registry_appendix', []) as $block)

{{ $block['heading'] ?? 'Capability' }}

    @foreach ($block['bullets'] ?? [] as $line)
  • {{ $line }}
  • @endforeach
@endforeach

Extending this list

Ship a new POST route or flag → document it here in the same accordion, and optionally append structured bullets via config/control_panel.phpregistry_appendix so long prose stays out of Blade. Each appendix entry uses heading plus bullets (array of plain strings).