Entries filed under Allgemein

Setup XMPP Chat server for cruise ship WiFis without Internet

Posted on 19. Januar 2024 Comments

This will be useful if you’re on a cruise with friends or family but don’t want to pay the excessive fees for the onboard WiFi internet. If you’re going transatlantic or transpacific, it’s also possible that internet isn’t available 100% of the time or it’s super slow during the day. It’s much easier to just leave your MacBook running in your cabin and install one of many popular free & open source XMPP clients from the AppStore / Google Play Store, such as

These also support OTR and/or OMEO encryption, which only work on one device per user though.

As an XMPP server, I’ve used Prosody.

  1. To install with MacOS, use homebrew: brew tap prosody/prosody && brew install prosody
  2. Follow the prosody documentation to create a self signed certificate: https://prosody.im/doc/certificates. Use a .local domain.
  3. Depending on the WiFi, you might have to change from the default XMPP ports to some you’re allowed to use without purchasing internet access. These usually include 80 (HTTP), 443 (HTTPS) and 53 (DNS), as they can usually be accessed to purchase the internet in the first place (landing page, credit card details page etc). Prosody also has documentation for this under https://prosody.im/doc/http.
  4. Add your users manually with prosodyctl adduser johndoe@nomadcruise.local and give them random passwords
  5. Start prosody with prosodyctl start

404 with content using basic auth with nginx

Posted on 19. Juli 2022 Comments

When using e.g. Laravel Forge you can create a section of the website that’s restricted and uses HTTP Basic Access Authentication for access control. When the credentials aren’t entered correctly, the server returns a 401 error.

Basic Authentication prompt

However, when you would like to restrict a section that’s part of an application, whatever rules you defined in your /etc/nginx/sites-available/domain.tld.conf you have to now add to the new location section.


On a staging server, you want to restrict access to /register and /login. Head to servers/X/sites/Y/security URL by clicking on the server, then select a site and click on „Security“. These entries create 2 files in /etc/nginx/forge-conf/domain.tld/server where ID is the ID you can see in Forge..

  • .htpasswd-ID
  • protected_site-ID.conf
Security Rules in Laravel Forge

However, when you now navigate to e.g. /login and enter your credentials, you will see a 404 for /login in the debug console. A request has been made and returned the content – just with the wrong status code. This is happening because there is no file called „login“ in the webserver public folder and nginx hasn’t been instructed to use PHP for this particular location.

tl;dr: You need to add this line to the location entry in the .conf files:

try_files $uri $uri/ /index.php?$query_string;

The whole file now looks like this:

location /register {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/forge-conf/domain.tld/server/.htpasswd-42;
try_files $uri $uri/ /index.php?$query_string;

Send private end-to-end encrypted Broadcast notifications with Laravel through Pusher

Posted on 22. August 2021 Comments

Pusher Channels allow an end-to-end (e2e) encrypted mode for their private channels. If you’re using Pusher as the BROADCAST_DRIVER in Laravel, it’s easy to enable this not only for broadcasted events but also notifications, so you can ->notify() the user without enabling Pusher to see what the content of the message is.

This is assuming you set up the authentication callback routes/broadcasting.php and it’s reachable (by default under /broadcasting/auth.)

  1. Add receivesBroadcastNotificationsOn() for the Notifiable Model (e.g. User)
    public function receivesBroadcastNotificationsOn()
        // `private-` is added automatically
        return 'encrypted-App.Models.User.' . $this->id;

The default implementation would be for a normal (not e2e) private channel and just return the FQCN in dot notation, followed by the Model ID.

Simply adding encrypted- before the channel name you now choose (or stick with the default suffix as above) will signal to Laravel to encrypt the messages before sending them out to pusher.

2. Add a shared key to config/broadcasting.php

'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'useTLS' => true,
                'encryption_master_key_base64' => env('PUSHER_APP_E2E_MASTER_KEY_BASE64'),

The end2end encryption is done synchronously with a shared key, stored base64 encoded. Of course, it’s important to keep this key secret. This encryption does not provide PFS, meaning, if the key ever leaks all old messages can be decrypted. Therefore, it’s probably a good idea to rotate it regularly or possibly not even use the same key for every user by manually changing the config before sending the message.

You can securely generate a key on the commandline or use PHP:

// Commandline
$ openssl rand -base64 32

// PHP

3. Client side

The client side using a pusher library recognizes the private-encrypted prefix. On successful authentication against /broadcasting/auth (or your custom authentication route) the shared key is transmitted in the response and used by the client to decrypt messages sent on that channel. You don’t need to worry about key distribution.

4. Double Check in the pusher.com debug console

You should only be able to see the none and the cyphertext, but not the plaintext message. If you do, something isn’t setup correctly yet.

5. Misc

The event for notifications to listen to is .Illuminate\\Notifications\\Events\\BroadcastNotificationCreated – don’t forget the . in front of it.

Laravel Nova: Move cards in Resource Detail View

Posted on 3. August 2021 Comments

Laravel Nova currently (v.3.27.0) doesn’t allow for custom cards to be moved to a different position in the resource detail page. Cards and Metrics appear always on top of the resources details.

However, while a bit dirty, a solution can be to let it load that way and then use Javascript to cut and paste the div somewhere else like so:

  1. Give the card an id
    <card class="flex flex-col justify-center" id="custom-card">

2. Use vanilla JS to move it underneath the first div with the name of the resource + detail-component for the dusk attribute

let customCard = document.querySelector('#custom-card');
        if(customCard) {
            let anotherComponent = document.querySelector('div[dusk=resourcename-detail-component]');
            if(anotherComponent) {
            } else {
                console.error('Could not find another resourcename detail component')

Autoload class alias order in Laravel tinker

Posted on 12. Juli 2021 Comments

When using tinker or tinkerwell without use statements or FQCN it tries to guess which class you mean by going through the autoloaded classes alphabetically. This might not be the class you most often used though, e.g. it is more likely I’d like to use App\Models\User, not the Livewire component of the same name.

$ tinker
UserPsy Shell v0.10.8 (PHP 8.0.8 — cli) by Justin Hileman
>>> User::first()
[!] Aliasing 'User' to 'App\Http\Livewire\User' for this Tinker session.

PHP provides the class_alias function but e.g. writing your own Service Provider for this will not work.

class ClassAliasesProvider extends ServiceProvider
     * Class Aliases defaults for tinker / tinkerwell.dev
     * @return void
    public function boot()
        class_alias(User::class, 'User');

Instead, add your classes to the array in config(‚app.alias‘).

        // other default Laravel aliases
        'View' => Illuminate\Support\Facades\View::class,

        // Better autoloading for tinker / tinkerwell
        'User' => \App\Models\User::class,

The next time loading up tinker / tinkerwell, it will use the correct alias.

How to install curl with HTTP/3 and QUIC support on MacOS

Posted on 20. November 2020 Comments

Even if curl is installed via homebrew and not the MacOS default it does not automatically support HTTP/3 & QUIC. You will get this error message:

$ curl -vs --http3 https://quic.rocks:4433
option --http3: the installed libcurl version doesn't support this

After reading https://github.com/cloudflare/quiche/issues/319 and fiddling around with it a arrived at this solution:

$ wget https://raw.githubusercontent.com/cloudflare/homebrew-cloudflare/master/curl.rb
$ brew uninstall curl
$ brew install --HEAD -s curl.rb # takes a while
$ curl --version
curl 7.74.0-DEV
$ curl -vs --http3 https://quic.rocks:4433

Backup 2 harddrives with rsync

Posted on 24. August 2017 Comments


rsync -a -v -P --delete -h --stats /media/repat/hdd1/ /media/repat/hdd2


rsync -a -v -P --delete -h --stats /Volumes/hdd1/ /Volumes/hdd2


-a all (-r, -l, -t, -p, -g, -o, -D)
-v verbose
-P show progress and continue partial transfers
–delete delete extraneous files from destination dirs
–stats give some file-transfer stats

Note: the last / after hdd1 is important, so the contents of hdd1 are written directly into hdd2. If you forget, there will be a new folder called hdd1 in the folder hdd2


Erinnerungs Skripte gegen USB-Stick vergessen

Posted on 13. März 2011 Comments

Jeder kennt das Problem, man sitzt in der Uni/Bibliothek/Freunden/öffentlichen Computern, speichert seine Daten auf dem USB Stick…und schaltet dann einfach den PC aus und geht, ohne den USB-Stick mitzunehmen. Beim nächsten mal ist er natürlich weg. Der Artikel richtet sich eigentlich eher ein bisschen an die Systemadministratoren, die dann diese Skripte in ihre Windows Installationen integrieren können, oder Leute die das für ihren PC zuhause benutzen. Die Abfragen werden alle in ein .bat-Skript geschrieben, dass dann beim Abmelden ausgeführt wird.

Nützlich sind immer die Windows eigenen Tools. Mit dem Befehl:

fsutil fsinfo drivetype F:

bekommt man z.B. Informationen über das Laufwerk F: angezeigt, natürlich muss das entsprechend angepasst werden. Jenachdem wieviele USB-Sticks/Festplatten man gleichzeitig dran hat, ists praktisch am besten auch noch ein paar weitere Buchstaben abzufragen, schaden tut’s ja nicht.

Wie man sieht kommt hier das Wort Volume drin vor, wenn der USB-Stick nicht drin steckt bekommt man jedoch eine andere Meldung. Also kann man mit findstr nach diesem Wort suchen. Wenn es nicht vorkommt, steckt auch kein USB-Stick, so einfach. Mit >nul schreibt man die Ausgabe ins Nirvana, damit man beim Herunterfahren eine nette Ausgabe bekommt, und nicht lauter Status/Fehlermeldungen.

fsutil fsinfo drivetype F: |findstr /c:“Volume“>nul

Nun schreibt man das ganze zu einem Skript zusammen.