CluPilotCloud/database/migrations
nexxo 955f1b874f Deliver the processing agreement, and hold the proof it was accepted
Art. 28(3) GDPR wants a contract wherever personal data is processed on
somebody else's behalf, which is the whole of what this product does. "In
writing" there includes electronic form (Art. 28(9)), so a document the
customer can read plus a recorded acceptance is enough — no signature on
paper. The website already promises "AV-Vertrag inklusive", which means it
has to be obtainable without asking us for it. It was not obtainable at
all.

**The text is never this application's.** An operator uploads the document
their lawyer wrote, names the version, and publishes it; the measures ride
along as a second file, because they are an annex to the agreement and
"which measures applied when this customer accepted" has to have one
answer. Inventing the text here would have been worse than having none.

**Uploading and publishing are two acts.** Acceptance is per version, so
publishing leaves every customer who accepted the previous one outstanding
again — correct, and far too expensive to trigger by dropping a file on a
form. It goes through a confirmation modal that says exactly that (R23).

**The customer's side** is a card in the contract tab: read the agreement,
read the measures, one press to conclude it. What that press records is
what makes it evidence rather than a flag — the version, the moment, the
address it came from, and the login that pressed. Pressing twice is one
agreement (unique index, not a check somebody can forget), and a
superseded acceptance is kept rather than overwritten: it was true when it
was made, and the history is the point.

Nothing renders until a version is in force. A card offering an agreement
that does not exist is worse than the silence.

The files live on the private disk and are served through routes that
check who is asking — an agreement is not a public asset, and a guessable
URL to one would be a list of who our customers are. The customer route
takes no version parameter: which document applies is ours to say.

`dpa.manage` is its own capability on the OPERATOR guard. Whoever keeps
the platform running does not thereby decide what every customer is asked
to agree to — and a capability written under `web` since the 2026-07-29
move lands in a guard nothing authenticates against, which is how this one
first shipped answering 403 to a role that visibly had it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:56:57 +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
2026_07_29_130000_create_invoice_tables.php Lay the foundation for self-issued invoices: series, numbers, frozen documents 2026-07-29 00:57:06 +02:00
2026_07_29_140000_add_export_state_to_invoices.php Copy every invoice to the archive when it is issued, and notice when that fails 2026-07-29 02:32:08 +02:00
2026_07_29_150000_create_export_targets.php Let the export have as many destinations as somebody wants 2026-07-29 02:43:15 +02:00
2026_07_29_170000_add_layout_and_retention_to_export_targets.php Let a destination say how it is laid out and how long anything stays 2026-07-29 03:08:27 +02:00
2026_07_29_180000_add_grant_provenance_to_subscriptions.php Let a subscription or add-on be opened for free, with who and why on the row 2026-07-29 12:41:06 +02:00
2026_07_29_180001_add_grant_provenance_to_subscription_addons.php Let a subscription or add-on be opened for free, with who and why on the row 2026-07-29 12:41:06 +02:00
2026_07_29_180002_add_grant_plans_capability.php Let a subscription or add-on be opened for free, with who and why on the row 2026-07-29 12:41:06 +02:00
2026_07_29_200000_create_status_history.php Rebuild the status page as a status page 2026-07-29 12:45:18 +02:00
2026_07_29_210000_add_recommended_flag_to_plan_families.php Let the owner mark one plan as recommended, from the console 2026-07-29 14:22:22 +02:00
2026_07_29_210001_add_marketing_copy_to_plan_families.php Move each plan's marketing copy from the controller into the console 2026-07-29 14:31:44 +02:00
2026_07_29_220000_add_domain_verification_to_instances.php Prove a custom domain before serving it, and keep proving it 2026-07-29 14:47:51 +02:00
2026_07_29_230000_add_address_state_to_instances.php Serve the custom domain, not just announce it 2026-07-29 16:44:52 +02:00
2026_07_29_230000_move_instances_to_their_own_zone.php Re-apply what the last release commit silently reverted 2026-07-29 17:36:43 +02:00
2026_07_29_240000_add_pending_restart_to_instances.php Apply a bought plan change instead of only pricing it 2026-07-29 17:18:55 +02:00
2026_07_29_250000_add_instance_restart_capability.php Restart a machine, enforce the quota that was sold, end a route that ended 2026-07-29 18:28:28 +02:00
2026_07_29_260000_add_applied_quota_to_instances.php Restart a machine, enforce the quota that was sold, end a route that ended 2026-07-29 18:28:28 +02:00
2026_07_29_270000_link_invoices_to_the_period_they_bill.php Invoice every renewal, and tell Stripe when the package changes 2026-07-29 19:35:15 +02:00
2026_07_29_280000_track_the_stripe_price_a_contract_is_billed_at.php Invoice every renewal, and tell Stripe when the package changes 2026-07-29 19:35:15 +02:00
2026_07_29_290000_bill_modules_through_stripe.php Bill a booked module every month, and put it on the invoice 2026-07-29 20:22:02 +02:00
2026_07_29_300000_record_whether_a_customer_is_a_consumer.php Ask whether they are a consumer, and let one change their mind 2026-07-29 21:06:06 +02:00
2026_07_29_300001_add_the_withdrawal_window_to_contracts.php Ask whether they are a consumer, and let one change their mind 2026-07-29 21:06:06 +02:00
2026_07_30_090000_add_routed_backend_to_instances.php Fix nine defects in the provisioning pipelines 2026-07-30 01:34:55 +02:00
2026_07_30_100000_record_every_mail_that_left_the_system.php Keep a register of what was sent, and answer the customer from here 2026-07-29 21:02:36 +02:00
2026_07_30_110000_charge_the_price_that_is_advertised.php Charge the price the website shows, and hand a withdrawal back in full 2026-07-29 22:42:02 +02:00
2026_07_30_200000_read_the_support_mailbox_into_the_console.php Read the support mailbox into the console 2026-07-29 21:41:29 +02:00
2026_07_31_100000_write_the_answers_once_and_reuse_them.php Show the customer, and write the answers once 2026-07-29 22:37:30 +02:00
2026_07_31_120000_seed_the_answers_that_are_always_needed.php Ship the thirteen answers this business actually sends 2026-07-29 22:44:31 +02:00
2026_07_31_140000_record_a_cancellation_on_the_contract_too.php Stop charging for a service that has ended 2026-07-30 00:52:19 +02:00
2026_07_31_160000_move_host_api_tokens_onto_the_secrets_key.php Make host onboarding survive the machine it runs on 2026-07-30 00:54:16 +02:00
2026_07_31_180000_charge_the_setup_fee_that_is_advertised.php Say the same thing to the customer, the register and the bank 2026-07-30 01:30:24 +02:00
2026_07_31_200000_sell_the_net_price_to_a_reverse_charge_business.php Stop charging VAT to the customers who owe us none 2026-07-30 02:15:41 +02:00
2026_07_31_210000_one_row_per_stripe_price.php Put the price recognition back, all 3526 lines of it 2026-07-30 16:35:55 +02:00
2026_07_31_220000_warn_before_a_dormant_account_is_removed.php Accept terms instead of a start date, and fix the sender no server accepts 2026-07-30 15:22:35 +02:00
2026_07_31_230000_say_how_many_months_a_year_is_free.php Sell the year as well as the month, and say what it saves 2026-07-30 15:44:35 +02:00
2026_08_01_090000_take_the_billing_address_field_by_field.php Take the address by field, fix the customer type, ask why they leave 2026-07-30 16:41:32 +02:00
2026_08_01_091000_record_why_a_contract_was_cancelled.php Take the address by field, fix the customer type, ask why they leave 2026-07-30 16:41:32 +02:00
2026_08_01_100000_keep_the_processing_agreement_and_who_accepted_it.php Deliver the processing agreement, and hold the proof it was accepted 2026-07-30 16:56:57 +02:00
2026_08_01_101000_add_the_dpa_manage_permission.php Deliver the processing agreement, and hold the proof it was accepted 2026-07-30 16:56:57 +02:00