CluPilotCloud/database/migrations
nexxo 2609393e3a
tests / pest (push) Failing after 7m52s Details
tests / assets (push) Successful in 20s Details
tests / release (push) Has been skipped Details
Recognise the devices an account signs in from, and warn about a new one
Sessions lived in Redis, where they cannot be enumerated — one key per session,
no index by user — so "where am I signed in" and "end that one" were not
answerable at all. The database driver keeps a row per session and makes ending
one a delete. It costs every currently signed-in person one forced sign-in,
once, which is why it is happening now rather than later.

The warning is only worth anything if it is rare, so the two obvious signals
are deliberately not used. Not the IP: it changes on every train, every café,
every mobile handover, and behind a VPN it is not theirs to begin with. Not the
user-agent: Chrome ships every four weeks and the string changes each time,
which would be twelve warnings a year per customer, describing nothing. A
device is a long-lived signed cookie holding a random token, and nothing else
is consulted. The honest cost — cleared cookies, a private window, a second
browser — is written into the mail itself, because a reader who thinks the
warning is wrong stops reading the next one.

Three things the tests found or hold:

The first device on an account is new and must not warn. Replying to somebody
creating an account by telling them they signed in from a new device is noise
on the one message that has to stay worth reading.

token_hash was globally unique, and the shared-browser test failed on it. Two
people on one machine share one cookie and must each own a device row against
it; the obvious repair — issue a fresh token for the second — would orphan the
first person's row and warn them about their own desk. Unique is now per
identity.

Recording a device may never block a sign-in. A safeguard that locks the owner
out when it breaks has stopped being one, so the listener logs and swallows,
and a test drops the tables to prove it.

Sessions are linked by a uuid carried inside the session payload rather than by
the session id, which Laravel rotates on every sign-in and every regenerate().
Laravel's own sessions.user_id cannot carry the link either: it is filled from
the default guard, while operators and customers are two guards and two tables
(R21) — operator 1 and customer 1 would each be offered the other's sessions.

Also lands the shared mail layout the rest of the templates will use: tables
and inline styles for Outlook, no image and no web font because half of all
clients block remote content, and the button in --accent-active rather than the
brand orange, which is 2.9:1 against white.

Not tagged: the session list is not built yet, and without a tag no server can
install this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:28:34 +02:00
..
0001_01_01_000000_create_users_table.php chore: bootstrap CluPilot control-plane (Laravel 13, Docker stack) 2026-07-25 00:21:30 +02:00
0001_01_01_000001_create_cache_table.php chore: bootstrap CluPilot control-plane (Laravel 13, Docker stack) 2026-07-25 00:21:30 +02:00
0001_01_01_000002_create_jobs_table.php chore: bootstrap CluPilot control-plane (Laravel 13, Docker stack) 2026-07-25 00:21:30 +02:00
2026_07_24_224530_add_two_factor_columns_to_users_table.php feat(portal): Fortify auth + Login/2FA/Dashboard + component kit 2026-07-25 01:20:25 +02:00
2026_07_24_224531_create_passkeys_table.php feat(portal): Fortify auth + Login/2FA/Dashboard + component kit 2026-07-25 01:20:25 +02:00
2026_07_25_062321_add_is_admin_to_users_table.php feat(admin): dark Tactical-Terminal operator console 2026-07-25 08:35:03 +02:00
2026_07_25_070001_create_hosts_table.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
2026_07_25_070002_create_provisioning_runs_table.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
2026_07_25_070003_create_provisioning_step_events_table.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
2026_07_25_070004_create_run_resources_table.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
2026_07_25_070005_add_unique_wg_ip_to_hosts_table.php fix(engine): address Codex round 2 (poll budget, host error state, wg race) 2026-07-25 10:26:37 +02:00
2026_07_25_070006_add_unique_public_ip_to_hosts_table.php fix(engine): one host per public IP (unique constraint + validation) 2026-07-25 10:43:30 +02:00
2026_07_25_080001_create_customers_table.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
2026_07_25_080002_create_orders_table.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
2026_07_25_080003_create_instances_table.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
2026_07_25_080004_create_dns_records_table.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
2026_07_25_080005_create_backups_table.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
2026_07_25_080006_create_monitoring_targets_table.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
2026_07_25_080007_create_onboarding_tasks_table.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
2026_07_25_080008_add_node_to_hosts_table.php feat(engine-b): 15-step customer pipeline + DNS/Traefik services 2026-07-25 11:50:26 +02:00
2026_07_25_080009_add_guest_ip_to_instances_table.php fix(engine-b): address Codex (paid-only stripe, guest routing, absolute disk) 2026-07-25 12:05:31 +02:00
2026_07_25_080010_add_unique_email_to_customers_table.php fix(engine-b): address Codex round 5 (capacity release, activate-last, guest-ip poll, email unique) 2026-07-25 12:19:31 +02:00
2026_07_25_090001_create_datacenters_table.php fix: backfill datacenters on migrate; resolve billing customer by user link 2026-07-25 13:53:06 +02:00
2026_07_25_090002_add_type_to_orders_table.php feat(portal): billing page — current plan, upgrades, extra storage, add-ons 2026-07-25 13:36:52 +02:00
2026_07_25_090003_add_user_id_to_customers_table.php feat(admin): impersonate customer portal — session login + return banner 2026-07-25 13:46:48 +02:00
2026_07_25_090004_make_customer_identity_unique.php fix(billing): hold only what cannot be matched, and let migrations roll back 2026-07-26 13:57:11 +02:00
2026_07_25_090005_add_profile_and_branding_to_customers.php feat(portal): settings page — company profile, branding (logo+colors), cancel package, close account 2026-07-25 14:36:52 +02:00
2026_07_25_090006_add_cancellation_to_instances.php feat(portal): settings page — company profile, branding (logo+colors), cancel package, close account 2026-07-25 14:36:52 +02:00
2026_07_25_090007_create_seats_table.php feat(portal): settings page — company profile, branding (logo+colors), cancel package, close account 2026-07-25 14:36:52 +02:00
2026_07_25_090008_add_datacenter_fk_to_hosts.php fix(admin): enforce hosts.datacenter FK (restrictOnDelete) — no deactivation side-effect 2026-07-25 15:00:38 +02:00
2026_07_25_133845_create_permission_tables.php feat(admin): staff RBAC (spatie) + admin settings page 2026-07-25 15:47:42 +02:00
2026_07_25_133900_seed_roles_and_permissions.php Make the RBAC move retry-safe and order-safe, and round-trip permissions 2026-07-28 13:31:34 +02:00
2026_07_25_140001_create_maintenance_windows.php feat(admin): maintenance windows — schedule once, assign many hosts, notify 2026-07-25 15:53:50 +02:00
2026_07_25_140002_add_claimed_at_to_maintenance_notifications.php fix(admin): in-flight claim (claimed_at) for exactly-once maintenance send; scope permission rollback 2026-07-25 16:38:44 +02:00
2026_07_25_200000_create_vpn_peers_table.php fix(vpn): stop revoked accesses coming back and stale jobs undoing intent 2026-07-25 21:41:03 +02:00
2026_07_25_200001_add_vpn_manage_capability.php feat(admin): VPN access management with live peer state 2026-07-25 21:38:12 +02:00
2026_07_25_210000_add_ownership_to_vpn_peers.php fix(vpn): classify adopted peers as system, not staff; drop duplicate env line 2026-07-25 22:34:18 +02:00
2026_07_25_210001_split_vpn_capabilities.php feat(vpn): ownership, a Developer role, and password-gated config retrieval 2026-07-25 22:31:54 +02:00
2026_07_25_220000_create_app_settings_table.php feat(site): switch the public website and portal off from the console 2026-07-25 23:10:27 +02:00
2026_07_25_220001_add_site_manage_capability.php feat(site): switch the public website and portal off from the console 2026-07-25 23:10:27 +02:00
2026_07_25_230000_create_instance_traffic_table.php feat(traffic): meter the monthly allowance, show it, throttle instead of blocking 2026-07-25 23:33:47 +02:00
2026_07_25_230001_add_traffic_addons_to_instances.php feat(traffic): meter the monthly allowance, show it, throttle instead of blocking 2026-07-25 23:33:47 +02:00
2026_07_25_240000_add_dns_name_to_hosts_table.php feat(hosts): give each host a DNS name under the public zone 2026-07-25 23:49:18 +02:00
2026_07_26_000000_add_instance_admin_capability.php feat(admin): administrator access to a customer's Nextcloud 2026-07-26 05:30:59 +02:00
2026_07_26_010000_add_vat_verification_to_customers.php fix(billing): a self-declared VAT ID must not zero the tax 2026-07-26 09:32:42 +02:00
2026_07_26_010001_bind_vat_verification_to_value.php fix(billing): verification vouches for a value, not for a row 2026-07-26 09:34:40 +02:00
2026_07_26_020000_create_subscriptions_table.php feat(billing): immutable subscription snapshots and pro-rata plan changes 2026-07-26 09:49:48 +02:00
2026_07_26_020001_add_tier_to_subscriptions.php fix(billing): upgrade or downgrade is decided by rank, not by price 2026-07-26 09:52:28 +02:00
2026_07_26_030000_link_subscriptions_to_orders.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
2026_07_26_040000_create_plan_catalogue_tables.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
2026_07_26_040001_link_subscriptions_to_plan_versions.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
2026_07_26_040002_add_plan_version_to_orders.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
2026_07_26_050000_add_plans_manage_capability.php feat(admin): a console for creating, pricing and scheduling plans 2026-07-26 12:41:51 +02:00
2026_07_26_060000_create_subscription_addons_table.php feat(billing): a proof register, and modules frozen at their booked price 2026-07-26 13:07:34 +02:00
2026_07_26_060001_create_subscription_records_table.php feat(billing): Stripe owns the billing cycle, we own capability 2026-07-26 13:36:28 +02:00
2026_07_26_070000_link_subscriptions_to_stripe.php feat(billing): Stripe owns the billing cycle, we own capability 2026-07-26 13:36:28 +02:00
2026_07_26_070001_one_register_entry_per_billing_event.php feat(billing): Stripe owns the billing cycle, we own capability 2026-07-26 13:36:28 +02:00
2026_07_26_070002_track_last_applied_stripe_event.php feat(billing): Stripe owns the billing cycle, we own capability 2026-07-26 13:36:28 +02:00
2026_07_26_070003_create_stripe_pending_events_table.php feat(billing): Stripe owns the billing cycle, we own capability 2026-07-26 13:36:28 +02:00
2026_07_27_040000_record_when_monitoring_last_answered.php Move the console off /admin, give the status page its own address, and measure monitoring 2026-07-27 06:05:40 +02:00
2026_07_27_060000_create_app_secrets_table.php Manage the Stripe key from the console, behind a password and a test 2026-07-27 09:01:16 +02:00
2026_07_27_060100_add_secrets_capability.php Manage the Stripe key from the console, behind a password and a test 2026-07-27 09:01:16 +02:00
2026_07_27_150000_create_instance_metrics_table.php Measure what the template draws 2026-07-27 16:29:28 +02:00
2026_07_27_160000_add_checks_to_instance_metrics.php Measure availability, and let a customer move down again 2026-07-27 16:41:15 +02:00
2026_07_27_180000_create_support_requests_table.php Editing in modals, an update button that is not gated on a stale reading, and a support page that is real 2026-07-27 17:55:49 +02:00
2026_07_28_090000_create_mailboxes_table.php Give every sending address a record of its own 2026-07-27 21:25:35 +02:00
2026_07_28_095000_add_authenticates_to_mailboxes_table.php Let a mailbox send without a password when it never needed one 2026-07-28 05:54:21 +02:00
2026_07_28_100000_seed_mailboxes_from_environment.php Trust an explicitly configured mail host or port over the placeholder 2026-07-28 07:10:53 +02:00
2026_07_28_110000_add_mail_manage_permission.php Stop the mailbox password save from crashing without SECRETS_KEY 2026-07-28 02:22:36 +02:00
2026_07_29_090000_create_operators_table.php Give the people who run CluPilot a table of their own 2026-07-28 09:20:54 +02:00
2026_07_29_100000_move_rbac_to_operator_guard.php Preflight every customer conflict before this migration mutates anything 2026-07-28 16:07:11 +02:00
2026_07_29_110000_add_facility_to_datacenters_table.php Let a datacenter code be corrected while nothing depends on it, and say which building 2026-07-28 22:25:21 +02:00
2026_07_29_120000_add_admin_password_to_instances_table.php Stop mailing the initial admin password, hold it in the panel until noted 2026-07-28 23:19:20 +02:00
2026_07_29_120000_create_sessions_and_device_tables.php Recognise the devices an account signs in from, and warn about a new one 2026-07-28 23:28:34 +02:00