9 Commits (6b8412f0c394981c091fdba767fc2defe3627124)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
3a4324fb6f |
Give people a way back in, and put the URL in English
tests / pest (push) Failing after 7m54s
Details
tests / assets (push) Successful in 21s
Details
tests / release (push) Has been skipped
Details
── The hole ──────────────────────────────────────────────────────────────── There was no password reset. Fortify's feature was commented out, so there was no link on the sign-in form, no page and no route — a customer who forgot their password was locked out of their own cloud until somebody opened a shell. On a product whose selling point is that you can ring somebody, that is a support call a week and an embarrassing one. Two pages of ours under Fortify's route names (R1/R2), because with views off Fortify registers only the POST endpoints. The mail is a Mailable in this product's design rather than the framework's MailMessage: somebody who has just been locked out is exactly the person a phishing mail is aimed at, and a message that looks nothing like the rest of our post is one they cannot check — ours carries the footer that names our domains. Three decisions worth stating, each with a test: The answer is identical whether the address is known or not. "No account with that address" turns the form into a way of finding out who is a customer. The reset kills every other session. Whoever knew the old password may still be signed in somewhere, and a reset that leaves them there has fixed nothing. It does NOT sign the visitor in. The link arrived by email, and a mailbox somebody else can read would otherwise be a session somebody else gets. ── /sicherheit → /security ───────────────────────────────────────────────── R13: paths are English. Mine was not. The old path stays as a permanent redirect because it has already gone out in mail footers. ── One wordmark ──────────────────────────────────────────────────────────── "Sometimes it says Cloud and sometimes it does not" — correct. It was on the footer, the placeholder and the maintenance screen, and absent from the header, the sidebar and the sign-in plate. The product is called CluPilot Cloud; that is the name on the invoices and in every mail, so it is now the name everywhere. The console sidebar takes the small size so the lockup, the ADMIN badge and the close button still share one line (R18). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
f8874f32ea |
Put customer instances on their own domain, and say which domains are ours
tests / pest (push) Failing after 8m1s
Details
tests / assets (push) Successful in 20s
Details
tests / release (push) Has been skipped
Details
── The zone ──────────────────────────────────────────────────────────────── A fresh install now addresses customer instances under clupilot.cloud rather than clupilot.com. Not branding: a Nextcloud is third-party software that strangers sign in to, and on the same registrable domain as the portal it shares cookie scope, document.domain and CAA with it. The same reason googleusercontent.com and vercel.app exist. Existing installations keep whatever the console says — the zone has been a setting all along. `clupilot:check-zone <zone>` reports what a change would touch before anything is touched: the DNS records, the monitoring targets, the certificates and Nextcloud's trusted_domains all carry the old name and the setting reaches none of them. With no instances in service it says so and stops. Changing it later is a migration with an outage, and the command says that too. ── Which addresses are ours ──────────────────────────────────────────────── A new public page at /sicherheit: the domains that belong to us, the rule for reading an address bar, what we never do, and — the part that matters — what to do if you have already typed your password into a copy of our sign-in form. Change it, end every other session, change it wherever else you use it, tell us. In that order. Reachable without an account, deliberately: somebody who has just given their password away is signed in nowhere, and the page they need cannot be behind the thing they lost. Linked from the three places a person is when the question comes up: every mail footer (the mail is the vector), the sign-in form (this is the page a phishing kit copies, so the real one says which host you are on), and the site footer. The domain list is DERIVED from configuration and keyed by what each domain is for. A list typed into a page would eventually tell a customer that a phishing domain is one of ours, or that one of ours is not — the same two-sources-of-truth failure that had the price sheet promising an address on a domain the company does not own. The operator console is deliberately absent from it: naming it on a public page tells an attacker where to aim. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
17a6fabcc2 |
One wordmark, one typeface, and the address the server actually issues
tests / pest (push) Failing after 11m28s
Details
tests / assets (push) Successful in 19s
Details
tests / release (push) Has been skipped
Details
Reported as "logo and font are different everywhere" — maintenance page, customer panel, console, homepage. They were. Counting them up: the site header set the name in ink at -0.02em, the sidebar split it into "Clu" plus an orange "Pilot", the sign-in plate set it white at a third size, the customer two-factor page set it in MONO with no mark at all, the error pages and the placeholder each inlined their own — and the maintenance screen had no wordmark whatsoever. Nine surfaces, seven ideas of the brand. There is one now: resources/views/components/ui/brand.blade.php. Mark plus wordmark in one piece, in ink, with an optional muted "Cloud". No orange half — that made the accent a permanent fixture instead of something used sparingly. The four places that cannot use a Blade component (the two self-contained pages, the error layout, the mail layout) inline it and are held to the same shape by a test. The typeface. The console loads IBM Plex through Vite; the 503 page has no stylesheet at all, so the maintenance screen — the one page shown when somebody is already worried — was rendering in the system sans. It declares the fonts itself now. And the bold weight was missing from public/fonts entirely: the placeholder and the error pages both asked for ibm-plex-sans-latin-700-normal.woff2, which was not there. A @font-face pointing at nothing fails silently, which is why nobody noticed. The error pages carried the same two faults the placeholder shipped with: an empty gradient tile where the mark belongs, and `gap` on an inline-flex row spacing "Clu", "Pilot" and "Cloud" apart as three separate words. And the price sheet promised "Adresse auf clupilot.cloud" — a domain the company does not own — while provisioning was issuing addresses under whatever CLUPILOT_DNS_ZONE says. It is assembled from that setting now, the same reason the prices are read from the catalogue rather than typed into the page. welcome.blade.php removed: Laravel's leftover, reachable from no route. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
5b8c28595a |
Require a confirmed address before an account can use anything
Registration opened an account that could sign in immediately on an address nobody had proved was theirs — and anybody can type a stranger's into a registration form. Both halves were commented out: the Fortify feature and MustVerifyEmail on the model. `verified` sits on the whole portal group rather than on the pages that spend money. An unconfirmed address is not a billing problem to be caught at checkout; it is an account that may not belong to the person holding it, and the servers, the users and the backups behind it are worth as much to whoever typed the address as to whoever owns it. The mail is a Mailable in this product's design rather than Laravel's notification, which would arrive with the framework's logo, button and footer as the first thing anybody ever receives from CluPilot. It also has to leave through the SYSTEM mailbox like every other account mail, which the notification path does not do on its own. The link is signed and expires, and the address is part of what is signed — so a link issued to a mistyped address stops working the moment the address is corrected, rather than confirming an address nobody can read. Tests cover the tampered link, the expired one, the corrected address, and one account trying to confirm another. The waiting page is ours (Fortify has views off) and offers the only two things that move somebody forward: send it again, or sign out and register with the address they meant. The resend is throttled, because otherwise it is a button that makes the server send mail to an arbitrary address as fast as it can be clicked. Somebody already confirmed is redirected away from it — the `verified` middleware only guards the other direction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
3f318c3f6a | Give the console a front door of its own | |
|
|
6376f5e9fa |
One design system for every surface
The console was a second product. admin-tokens.css held a complete dark set — its own surfaces, its own orange, its own status triad — so every shared component had to work in two worlds, and the two halves of the application drifted apart in ways nobody could see from inside either one. That file now carries density and nothing else: smaller type, tighter rows, tighter corners. Colour comes from one place. The tokens themselves are rebuilt around what the redesign settled on: - Radii scaled to object size (9/11/16/22) instead of one value everywhere, which is what made the interface read as a design-system specification rather than as a product. - Warm, broad, low shadows. A neutral grey drop shadow belongs to a different design language and reads as cold on this ground. - --accent-press, which several rules already referenced and nothing defined. An undefined var() does not make a browser skip the declaration; it computes to , and for background-color that is transparent — the accent button lost its fill on hover and left white text on white. - No serif. A serif headline is what made every page read as a document, which is the impression the whole redesign exists to remove. --font-serif now points at the sans so nothing breaks while call sites are cleaned up. The two shells are one shell with two configurations, and both finally have a mobile navigation: the sidebar used to simply disappear below 900px, leaving no way to move around the application at all on a phone. The customer dashboard is bound to real records — instance, seats, traffic, backups, contract — instead of the fixtures it shipped with. Where something is not measured, storage consumption, it says what was contractually agreed rather than inventing a figure. This is the sheet a customer forwards to their auditor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
b844ff377d |
Rebuild the public site as a document, and read prices from the catalogue
tests / pest (push) Successful in 7m30s
Details
tests / assets (push) Successful in 19s
Details
tests / release (push) Successful in 4s
Details
The marketing page was a generic centred hero in the system font stack: it neither used the design system the console is built on nor looked like the product it sells. Rebuilt around what CluPilot actually promises — security you can produce evidence for — so the page reads as a controlled technical document: a specification plate instead of a hero image, numbered sections, an audit register with rhythm and proof per measure, and a price sheet rather than floating cards. IBM Plex Serif joins Sans and Mono as the display voice, self hosted as static files so the public page renders even without the Vite build. The prices were written into the page by hand, and the page and the catalogue had already drifted apart on three of four plans: the site advertised 249 € for a plan the checkout charges 399 € for. The sheet now reads the catalogue, like every other caller — including the currency, which is configurable and was also hard-coded here. The catalogue fails loudly on purpose; a public website must not. A broken or overlapping catalogue is caught in the controller alone: the page still renders, the sheet is replaced by "on request", and nobody is quoted a number the checkout would not honour. The sign-in and registration panels shared a copy-pasted orange gradient. They now share one component and the same ink plate as the site, so the two halves of the product no longer look like two companies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|
|
|
88957f8f57 |
feat(auth): enterprise split login + signup (Fortify registration) via frontend-design
- login/register redesigned as a split brand-panel + clean form (Apple-ish enterprise look), cross-linked; brand panel uses accent gradient + tokens - Fortify registration enabled; /register full-page Livewire + RegisterTest Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
|
|
|
254a7d46a0 |
feat(portal): Fortify auth + Login/2FA/Dashboard + component kit
Backend (Fortify): - laravel/fortify with TOTP two-factor + recovery codes; User uses TwoFactorAuthenticatable; 2FA credentials hidden from serialization. - Views off — pages are full-page class-based Livewire components (R1/R2); Fortify handles POST actions. Home redirect -> /dashboard. v1 scope: login + 2FA only (no public register/reset/passkeys). Seeder gated to local/testing. Component kit (Blade, token-based, a11y): - button, input, checkbox, alert, card, badge, stat-tile, otp-input (Alpine, auto-advance/paste, -safe submit), progress-stepper, nav-item, icon (Lucide), plus layouts/portal-app app-shell (sidebar drawer + topbar + menu). Screens (localized DE/EN, R16): - Login (form -> login.store), Two-factor challenge (OTP + recovery fallback), Dashboard (KPI stat tiles, instance card, provisioning stepper fixtures, activity). Routes English (R13). Tests + verification: - Pest: 14 green (login ok/invalid/throttle, dashboard guard, component render). - R12 browser (Puppeteer, prod assets): /, /login, /two-factor-challenge and the authenticated /dashboard all HTTP 200 with ZERO console errors; login flow verified end-to-end. - Test isolation fixed (force test env over injected .env). - Reviewed with Codex (R15): 4 rounds, all findings fixed, final pass clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |