Matrix Clients: Cinny and Fluffychat

Published: Jan 23, 2024 by Isaac Johnson

Cinny is “Yet another Matrix client” as their site puts it. They focus on elegance and simplicity. FluffyChat is a Matrix client with mobile and web versions.

I’ve wanted to circle back on them ever since using Element. They are the other two listed on the “Featured Clients” page of matrix.org

/content/images/2024/01/cinny-17.png

Cinny

The first try will be to just use docker to pull and launch it

We’ll start by pulling the Docker image locally

builder@builder-T100:~$ docker pull ghcr.io/cinnyapp/cinny:latest
latest: Pulling from cinnyapp/cinny
9398808236ff: Already exists
7b8bdebbb770: Pull complete
a2a4fe64baa0: Pull complete
0777b518fc6e: Pull complete
63f4060a8ef3: Pull complete
9cbe387ec693: Pull complete
48703ecfcf80: Pull complete
e6a5b5694466: Pull complete
bad39fabacf7: Pull complete
4c4ed36622de: Pull complete
Digest: sha256:419e4a03cb9e3b1547eeb0e7e14bf6b9ebc42bd0b499bbebcc5b489f2942f489
Status: Downloaded newer image for ghcr.io/cinnyapp/cinny:latest
ghcr.io/cinnyapp/cinny:latest

Now I can launch it

$ docker run -d -p 8088:80 --name cinny --restart always ghcr.io/cinnyapp/cinny:latest
9fe031d651422ed2985eacdf703a23dea559329211bfff0c871cccf63451986a

/content/images/2024/01/cinny-01.png

builder@builder-T100:~$ docker ps
CONTAINER ID   IMAGE                                                            COMMAND                  CREATED         STATUS                 PORTS                                                 NAMES
9fe031d65142   ghcr.io/cinnyapp/cinny:latest                                    "/docker-entrypoint.…"   9 minutes ago   Up 8 minutes           0.0.0.0:8088->80/tcp, :::8088->80/tcp                 cinny

Let’s expose it externally

$ cat r53-cinny.json
{
    "Comment": "CREATE cinny fb.s A record ",
    "Changes": [
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "cinny.freshbrewed.science",
          "Type": "A",
          "TTL": 300,
          "ResourceRecords": [
            {
              "Value": "75.73.224.240"
            }
          ]
        }
      }
    ]
  }
$ aws route53 change-resource-record-sets --hosted-zone-id Z39E8QFU0F9PZP --change-batch file://r53-cinny.json
{
    "ChangeInfo": {
        "Id": "/change/C0864129N62OKYFJ8LDK",
        "Status": "PENDING",
        "SubmittedAt": "2024-01-02T01:33:38.091Z",
        "Comment": "CREATE cinny fb.s A record "
    }
}

I’ll use Kubernetes to forward out to Docker using the exposed port 8088.

$ cat ingresscinny.yaml
---
apiVersion: v1
kind: Endpoints
metadata:
  name: cinny-external-ip
subsets:
- addresses:
  - ip: 192.168.1.100
  ports:
  - name: cinny
    port: 8088
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: cinny-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: cinny
    port: 80
    protocol: TCP
    targetPort: 8088
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: cinny-external-ip
  generation: 1
  labels:
    app.kubernetes.io/instance: cinnyingress
  name: cinnyingress
spec:
  rules:
  - host: cinny.freshbrewed.science
    http:
      paths:
      - backend:
          service:
            name: cinny-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - cinny.freshbrewed.science
    secretName: cinny-tls

$ kubectl apply -f ingresscinny.yaml
endpoints/cinny-external-ip created
service/cinny-external-ip created
ingress.networking.k8s.io/cinnyingress created

Which now loads without issue using TLS

/content/images/2024/01/cinny-02.png

We can compare Element (left) with Cinny (right)

/content/images/2024/01/cinny-03.png

I think the UI on Cinny is quite nice, but I noted it didn’t include the Jitsi video conferencing that Element has.

I tested private messaging which works fine

/content/images/2024/01/cinny-04.png

Fluffychat

FluffyChat is another nice client with an App for phones and web. You can find the code and steps in Github.

My first step was to try and build the Dockerfile in the repo

builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ docker build -t fluffychat:0.0.1 .
[+] Building 2.7s (5/5) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                                          0.0s
 => => transferring dockerfile: 38B                                                                                                                                                                                                                                           0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                             0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                               0.0s
 => [internal] load metadata for docker.io/library/nginx:alpine                                                                                                                                                                                                               0.8s
 => ERROR [internal] load metadata for ghcr.io/cirruslabs/flutter:latest                                                                                                                                                                                                      1.8s
 => [auth] cirruslabs/flutter:pull token for ghcr.io                                                                                                                                                                                                                          0.0s
------
 > [internal] load metadata for ghcr.io/cirruslabs/flutter:latest:
------
failed to solve with frontend dockerfile.v0: failed to create LLB definition: failed to authorize: rpc error: code = Unknown desc = failed to fetch oauth token: unexpected status: 403 Forbidden

I guess you need to periodically login to GHCR

builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ echo $CR_PAT | docker login ghcr.io -u idjohnson --password-stdin
Login Succeeded

Then I couldn’t find yq

builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ docker build -t fluffychat:0.0.1 .
[+] Building 50.5s (13/16)
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                                          0.0s
 => => transferring dockerfile: 38B                                                                                                                                                                                                                                           0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                             0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                               0.0s
 => [internal] load metadata for docker.io/library/nginx:alpine                                                                                                                                                                                                               2.1s
 => [internal] load metadata for ghcr.io/cirruslabs/flutter:latest                                                                                                                                                                                                            5.2s
 => [auth] cirruslabs/flutter:pull token for ghcr.io                                                                                                                                                                                                                          0.0s
 => [internal] load build context                                                                                                                                                                                                                                             7.0s
 => => transferring context: 603.82MB                                                                                                                                                                                                                                         6.9s
 => [stage-1 1/3] FROM docker.io/library/nginx:alpine@sha256:a59278fd22a9d411121e190b8cec8aa57b306aa3332459197777583beb728f59                                                                                                                                                 3.4s
 => => resolve docker.io/library/nginx:alpine@sha256:a59278fd22a9d411121e190b8cec8aa57b306aa3332459197777583beb728f59                                                                                                                                                         0.0s
 => => sha256:a59278fd22a9d411121e190b8cec8aa57b306aa3332459197777583beb728f59 6.70kB / 6.70kB                                                                                                                                                                                0.0s
 => => sha256:529b5644c430c06553d2e8082c6713fe19a4169c9dc2369cbb960081f52924ff 11.72kB / 11.72kB                                                                                                                                                                              0.0s
 => => sha256:c926b61bad3b94ae7351bafd0c184c159ebf0643b085f7ef1d47ecdc7316833c 3.40MB / 3.40MB                                                                                                                                                                                0.3s
 => => sha256:2d2a2257c6e9d2e5b50d4fbeb436d8d2b55631c2a89935a425b417eb95212686 2.19kB / 2.19kB                                                                                                                                                                                0.0s
 => => sha256:fed54a1dc458a7f591fa1c986669998655ad54d260d53691c8ef4841185883d4 1.90MB / 1.90MB                                                                                                                                                                                0.3s
 => => sha256:d4735778d47c0be8db66c446904aa2ba47f3e7509c0c9c3985ecb3b96bb7179f 629B / 629B                                                                                                                                                                                    0.3s
 => => extracting sha256:c926b61bad3b94ae7351bafd0c184c159ebf0643b085f7ef1d47ecdc7316833c                                                                                                                                                                                     0.2s
 => => sha256:8695c106552e600555fefc1bc2b299b420c52583bbf537e6c0468bc7821a3f7b 955B / 955B                                                                                                                                                                                    0.4s
 => => sha256:9e50a0e580b1e5240c8bf21f791b11fb7a8f3c04249f5db56f1bc72f2fa73929 1.21kB / 1.21kB                                                                                                                                                                                0.4s
 => => sha256:dffa16519b51a7abc6df8837b2ceffb699eedd09394ecfeff363ae5321cb7ad2 366B / 366B                                                                                                                                                                                    0.6s
 => => sha256:5ddd532e9cec09472cd07e594cb6dce78c43ba5248310263f8f766c74b9fb6ae 1.40kB / 1.40kB                                                                                                                                                                                0.5s
 => => sha256:fe117667dcd024947ead1f25ad99a5e522efcf3b7dbd0752b6fb5e73feffb407 12.65MB / 12.65MB                                                                                                                                                                              0.8s
 => => extracting sha256:fed54a1dc458a7f591fa1c986669998655ad54d260d53691c8ef4841185883d4                                                                                                                                                                                     0.4s
 => => extracting sha256:d4735778d47c0be8db66c446904aa2ba47f3e7509c0c9c3985ecb3b96bb7179f                                                                                                                                                                                     0.1s
 => => extracting sha256:8695c106552e600555fefc1bc2b299b420c52583bbf537e6c0468bc7821a3f7b                                                                                                                                                                                     0.1s
 => => extracting sha256:dffa16519b51a7abc6df8837b2ceffb699eedd09394ecfeff363ae5321cb7ad2                                                                                                                                                                                     0.1s
 => => extracting sha256:9e50a0e580b1e5240c8bf21f791b11fb7a8f3c04249f5db56f1bc72f2fa73929                                                                                                                                                                                     0.1s
 => => extracting sha256:5ddd532e9cec09472cd07e594cb6dce78c43ba5248310263f8f766c74b9fb6ae                                                                                                                                                                                     0.1s
 => => extracting sha256:fe117667dcd024947ead1f25ad99a5e522efcf3b7dbd0752b6fb5e73feffb407                                                                                                                                                                                     0.5s
 => [builder 1/7] FROM ghcr.io/cirruslabs/flutter@sha256:d37cffe5cabbe96f1c59ba5f0d5166f3d7043324279858ba2982e36d1d7361dc                                                                                                                                                    24.6s
 => => resolve ghcr.io/cirruslabs/flutter@sha256:d37cffe5cabbe96f1c59ba5f0d5166f3d7043324279858ba2982e36d1d7361dc                                                                                                                                                             0.0s
 => => sha256:d37cffe5cabbe96f1c59ba5f0d5166f3d7043324279858ba2982e36d1d7361dc 856B / 856B                                                                                                                                                                                    0.0s
 => => sha256:461a1551032ea5375bfd2b7aa19b74b4c21e375361e96eafa22d80723fa652a2 1.44kB / 1.44kB                                                                                                                                                                                0.0s
 => => sha256:6664e9fe749c93a3cc112f0f82a35a8d108d3602adadf8bed6482165ba2a2582 7.61kB / 7.61kB                                                                                                                                                                                0.0s
 => => sha256:c5e70d3948ed7b8958a65e1f9b663debedbc130485f07741250ec265bbd06a4c 25.58MB / 25.58MB                                                                                                                                                                              4.5s
 => => sha256:486c3f4ba609c5cd84ac96c65edf7931aba0a8aea0a7661d2747c60bf5c7c4ee 309.34MB / 309.34MB                                                                                                                                                                           15.6s
 => => extracting sha256:c5e70d3948ed7b8958a65e1f9b663debedbc130485f07741250ec265bbd06a4c                                                                                                                                                                                     1.9s
 => => extracting sha256:486c3f4ba609c5cd84ac96c65edf7931aba0a8aea0a7661d2747c60bf5c7c4ee                                                                                                                                                                                     8.5s
 => [stage-1 2/3] RUN rm -rf /usr/share/nginx/html                                                                                                                                                                                                                            2.8s
 => [builder 2/7] RUN sudo apt update && sudo apt install curl jq -y                                                                                                                                                                                                         15.1s
 => [builder 3/7] COPY . /app                                                                                                                                                                                                                                                 4.0s
 => [builder 4/7] WORKDIR /app                                                                                                                                                                                                                                                0.0s
 => ERROR [builder 5/7] RUN ./scripts/prepare-web.sh                                                                                                                                                                                                                          0.4s
------
 > [builder 5/7] RUN ./scripts/prepare-web.sh:
#13 0.402 #!/bin/sh -ve
#13 0.402 rm -r assets/js/package
#13 0.403
#13 0.403 OLM_VERSION=$(cat pubspec.yaml | yq .dependencies.flutter_olm)
#13 0.403 ./scripts/prepare-web.sh: 4: yq: not found
------
executor failed running [/bin/sh -c ./scripts/prepare-web.sh]: exit code: 127

I tried a few ways to install yq including adding homebrew and snap and apt… in the end, just downloaded and extracted a linux x64 tgz from https://github.com/mikefarah/yq/releases and put it next to the Dockerfile. I then added a copy step to put it in /usr/bin

FROM ghcr.io/cirruslabs/flutter as builder
RUN sudo apt update && sudo apt install curl jq -y
COPY ./yq_linux_amd64 /usr/bin/yq
COPY . /app
WORKDIR /app
RUN ./scripts/prepare-web.sh
RUN flutter pub get
RUN flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps

FROM docker.io/nginx:alpine
RUN rm -rf /usr/share/nginx/html
COPY --from=builder /app/build/web /usr/share/nginx/html

This worked, but did take a long time to build

$ docker build -t fluffychat:0.0.1 .
[+] Building 108.3s (14/16)
[+] Building 164.9s (14/16)
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                                          0.0s
 => => transferring dockerfile: 463B                                                                                                                                                                                                                                          0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                             0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                               0.0s
 => [internal] load metadata for docker.io/library/nginx:alpine                                                                                                                                                                                                               2.5s
 => [internal] load metadata for ghcr.io/cirruslabs/flutter:latest                                                                                                                                                                                                            2.9s
 => [builder 1/8] FROM ghcr.io/cirruslabs/flutter@sha256:d37cffe5cabbe96f1c59ba5f0d5166f3d7043324279858ba2982e36d1d7361dc                                                                                                                                                     0.0s
 => [internal] load build context                                                                                                                                                                                                                                             0.2s
 => => transferring context: 14.01MB                                                                                                                                                                                                                                          0.1s[+] Building 165.0s (14/16)
[+] Building 167.3s (17/17) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                              0.0s.
 => => transferring dockerfile: 463B                                                                                                                                                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                 0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/nginx:alpine                                                                                                                                                                                                   2.5s
 => [internal] load metadata for ghcr.io/cirruslabs/flutter:latest                                                                                                                                                                                                2.9s
 => [builder 1/8] FROM ghcr.io/cirruslabs/flutter@sha256:d37cffe5cabbe96f1c59ba5f0d5166f3d7043324279858ba2982e36d1d7361dc                                                                                                                                         0.0s
 => [internal] load build context                                                                                                                                                                                                                                 0.2s
 => => transferring context: 14.01MB                                                                                                                                                                                                                              0.1s
 => [stage-1 1/3] FROM docker.io/library/nginx:alpine@sha256:a59278fd22a9d411121e190b8cec8aa57b306aa3332459197777583beb728f59                                                                                                                                     0.0s
 => CACHED [stage-1 2/3] RUN rm -rf /usr/share/nginx/html                                                                                                                                                                                                         0.0s
 => CACHED [builder 2/8] RUN sudo apt update && sudo apt install curl jq -y                                                                                                                                                                                       0.0s
 => [builder 3/8] COPY ./yq_linux_amd64 /usr/bin/yq                                                                                                                                                                                                               0.2s
 => [builder 4/8] COPY . /app                                                                                                                                                                                                                                     3.2s
 => [builder 5/8] WORKDIR /app                                                                                                                                                                                                                                    0.0s
 => [builder 6/8] RUN ./scripts/prepare-web.sh                                                                                                                                                                                                                    1.3s
 => [builder 7/8] RUN flutter pub get                                                                                                                                                                                                                            40.7s
 => [builder 8/8] RUN flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps                                                                                                                                              117.4s
 => [stage-1 3/3] COPY --from=builder /app/build/web /usr/share/nginx/html                                                                                                                                                                                        0.2s
 => exporting to image                                                                                                                                                                                                                                            0.3s
 => => exporting layers                                                                                                                                                                                                                                           0.2s
 => => writing image sha256:ba0af52ba2e086499120d09d5dc2368ee65c1ce05b1128f2f9977169c2b7c3cf                                                                                                                                                                      0.0s
 => => naming to docker.io/library/fluffychat:0.0.1

I could now fire up a test

 builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ docker run -p 9090:80 fluffychat:0.0.1
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2024/01/03 12:37:59 [notice] 1#1: using the "epoll" event method
2024/01/03 12:37:59 [notice] 1#1: nginx/1.25.3
2024/01/03 12:37:59 [notice] 1#1: built by gcc 12.2.1 20220924 (Alpine 12.2.1_git20220924-r10)
2024/01/03 12:37:59 [notice] 1#1: OS: Linux 5.15.133.1-microsoft-standard-WSL2
2024/01/03 12:37:59 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/01/03 12:37:59 [notice] 1#1: start worker processes
2024/01/03 12:37:59 [notice] 1#1: start worker process 30
2024/01/03 12:37:59 [notice] 1#1: start worker process 31
2024/01/03 12:37:59 [notice] 1#1: start worker process 32
2024/01/03 12:37:59 [notice] 1#1: start worker process 33
2024/01/03 12:37:59 [notice] 1#1: start worker process 34
2024/01/03 12:37:59 [notice] 1#1: start worker process 35
2024/01/03 12:37:59 [notice] 1#1: start worker process 36
2024/01/03 12:37:59 [notice] 1#1: start worker process 37
2024/01/03 12:37:59 [notice] 1#1: start worker process 38
2024/01/03 12:37:59 [notice] 1#1: start worker process 39
2024/01/03 12:37:59 [notice] 1#1: start worker process 40
2024/01/03 12:37:59 [notice] 1#1: start worker process 41
2024/01/03 12:37:59 [notice] 1#1: start worker process 42
2024/01/03 12:37:59 [notice] 1#1: start worker process 43
2024/01/03 12:37:59 [notice] 1#1: start worker process 44
2024/01/03 12:37:59 [notice] 1#1: start worker process 45
172.17.0.1 - - [03/Jan/2024:12:38:13 +0000] "GET / HTTP/1.1" 200 2485 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "-"
172.17.0.1 - - [03/Jan/2024:12:38:13 +0000] "GET /splash/style.css HTTP/1.1" 200 674 "http://localhost:9090/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "-"
172.17.0.1 - - [03/Jan/2024:12:38:13 +0000] "GET /flutter.js HTTP/1.1" 200 14326 "http://localhost:9090/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "-"

/content/images/2024/01/cinny-05.png

It seemed rather stuck on the server picklist

/content/images/2024/01/cinny-06.png

I think I need to create a config.json and build again

builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ vi config.sample.json
builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ cp config.sample.json config.json
builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ vi config.json
builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ cat config.json
{
  "application_name": "FluffyChat for FB",
  "application_welcome_message": "Welcome to Fresh/Brewed",
  "default_homeserver": "matrix.freshbrewed.science",
  "web_base_url": "https://freshbrewed.science/",
  "privacy_url": "https://fluffychat.im/en/privacy.html",
  "render_html": true,
  "hide_redacted_events": false,
  "hide_unknown_events": false
}

builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ docker build -t fluffychat:0.0.2 .
[+] Building 30.0s (12/16)
 => [internal] load build definition from Dockerfile                                                                                                                                                                                                              0.0s
 => => transferring dockerfile: 38B                                                                                                                                                                                                                               0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                 0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/nginx:alpine                                                                                                                                                                                                   2.0s
 => [internal] load metadata for ghcr.io/cirruslabs/flutter:latest                                                                                                                                                                                                1.4s
 => [internal] load build context
 ...snip...

I also took a moment to fix the Dockerfile in a more re-usable way - that is, pull down yq during the build as opposed to needing to get a tgz download handy.

FROM ghcr.io/cirruslabs/flutter as builder
RUN sudo apt update && sudo apt install curl wget jq -y

WORKDIR /tmp
RUN wget https://github.com/mikefarah/yq/releases/download/v4.40.5/yq_linux_amd64.tar.gz
RUN tar -xzvf ./yq_linux_amd64.tar.gz
RUN mv yq_linux_amd64 /usr/bin/yq

COPY . /app
WORKDIR /app
RUN ./scripts/prepare-web.sh
RUN flutter pub get
RUN flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps

FROM docker.io/nginx:alpine
RUN rm -rf /usr/share/nginx/html
COPY --from=builder /app/build/web /usr/share/nginx/html

It did not set a default and still shows an error, but I did manage to just type in the homeserver and hit enter and it worked to login

builder@DESKTOP-QADGF36:~/Workspaces/fluffychat$ docker run -p 9090:80 fluffychat:0.0.3
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2024/01/03 12:54:35 [notice] 1#1: using the "epoll" event method
2024/01/03 12:54:35 [notice] 1#1: nginx/1.25.3
2024/01/03 12:54:35 [notice] 1#1: built by gcc 12.2.1 20220924 (Alpine 12.2.1_git20220924-r10)
2024/01/03 12:54:35 [notice] 1#1: OS: Linux 5.15.133.1-microsoft-standard-WSL2
2024/01/03 12:54:35 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/01/03 12:54:35 [notice] 1#1: start worker processes
2024/01/03 12:54:35 [notice] 1#1: start worker process 30
...

/content/images/2024/01/cinny-07.png

I tried updating the web/manifest.json

{
    "name": "FluffyChat",
    "short_name": "FluffyChat",
    "start_url": "matrix.freshbrewed.science",
    "default_homeserver": "matrix.freshbrewed.science",
    "display": "standalone",
    "background_color": "#0175C2",
    "theme_color": "#0175C2",
    "description": "The cutest messenger in the Matrix network",
    "orientation": "portrait-primary",
    "prefer_related_applications": false,
    "icons": [
        {
            "src": "icons/Icon-192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "icons/Icon-512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}

I tried forcing the config copy in the Dockerfile

FROM ghcr.io/cirruslabs/flutter as builder
RUN sudo apt update && sudo apt install curl wget jq -y

WORKDIR /tmp
RUN wget https://github.com/mikefarah/yq/releases/download/v4.40.5/yq_linux_amd64.tar.gz
RUN tar -xzvf ./yq_linux_amd64.tar.gz
RUN mv yq_linux_amd64 /usr/bin/yq

COPY . /app
WORKDIR /app
RUN ./scripts/prepare-web.sh
COPY config.json /app/config.json
RUN flutter pub get
RUN flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps

FROM docker.io/nginx:alpine
RUN rm -rf /usr/share/nginx/html
COPY --from=builder /app/build/web /usr/share/nginx/html

But I just could not get it to take the changes. I ultimately forced it by updating the .dart file which seems a bit extreme

/content/images/2024/01/cinny-08.png

which we can see

/content/images/2024/01/cinny-09.png

I was about to blame Fluffychat for not decrypting prior messages, but then I realized this is a ?feature? of the end-to-end built-in encryption.

The conversation I started yesterday from Cinny Web as my GMail identity on matrix.org just couldn’t decrypt outside of that session

/content/images/2024/01/cinny-10.png

Note: if you do want to backup messages, you can set aside the key under settings

/content/images/2024/01/cinny-14.png

Clearly nothing (by default) is stored on the server, so logging back into Cinny web as my GMail identity didn’t help. I would need to power up the laptop used yesterday to decrypt those messages

/content/images/2024/01/cinny-11.png

For the security minded, that is handy. But if you use chat for history, one can suggest busting into a new room if discussing something you want history on or transferring between clients.

I can see calls work just fine between Element and Fluffychat (but not Cinny)

/content/images/2024/01/cinny-12.png

Cinny shows the calls as “Unsupported message”

/content/images/2024/01/cinny-13.png

Lastly, in the themes area, we can tweak light and dark mode as well as font size. Here I switched to light mode with smaller fonts

/content/images/2024/01/cinny-15.png

Hosting

Let me push the local build to my CR

builder@DESKTOP-QADGF36:~$ docker tag fluffychat:0.0.6 harbor.freshbrewed.science/freshbrewedprivate/fluffychat:0.0.6
builder@DESKTOP-QADGF36:~$ docker push harbor.freshbrewed.science/freshbrewedprivate/fluffychat:0.0.6
The push refers to repository [harbor.freshbrewed.science/freshbrewedprivate/fluffychat]
8a4818a2a13a: Pushed
4fc9dfc985df: Pushed
2d33248ce9da: Pushed
9770295d804c: Pushed
62e59aa00d24: Pushed
769b844042ad: Pushed
4c6b2fc6378f: Pushed
c612d245f985: Pushed
3d49ee199a5c: Pushed
9fe9a137fd00: Pushed
0.0.6: digest: sha256:686cf5ec4c595080e7ce76171c881c4d209cbc8a0033c99b630555d964d6670d size: 2408

Now on the Dockerhost I’ll pull down that image

builder@builder-T100:~$ docker pull harbor.freshbrewed.science/freshbrewedprivate/fluffychat:0.0.6
0.0.6: Pulling from freshbrewedprivate/fluffychat
c926b61bad3b: Already exists
fed54a1dc458: Pull complete
d4735778d47c: Pull complete
8695c106552e: Pull complete
dffa16519b51: Pull complete
9e50a0e580b1: Pull complete
5ddd532e9cec: Pull complete
fe117667dcd0: Pull complete
89e33d70780f: Pull complete
438b8669ac0d: Pull complete
Digest: sha256:686cf5ec4c595080e7ce76171c881c4d209cbc8a0033c99b630555d964d6670d
Status: Downloaded newer image for harbor.freshbrewed.science/freshbrewedprivate/fluffychat:0.0.6
harbor.freshbrewed.science/freshbrewedprivate/fluffychat:0.0.6

I’ll pick a port and start it

builder@builder-T100:~$ sudo docker run -p 9095:80 --restart unless-stopped --name fluffychat -d harbor.freshbrewed.science/freshbrewedprivate/fluffychat:0.0.6
15c7521468041cb9c5ce26928ab44ddb324c1af353ce2eadd9330f1bf2329591

Just like Cinny, let’s create an A Record

$ cat r53-fluffychat.json
{
    "Comment": "CREATE fluffychat fb.s A record ",
    "Changes": [
      {
        "Action": "CREATE",
        "ResourceRecordSet": {
          "Name": "fluffychat.freshbrewed.science",
          "Type": "A",
          "TTL": 300,
          "ResourceRecords": [
            {
              "Value": "75.73.224.240"
            }
          ]
        }
      }
    ]
  }
$ aws route53 change-resource-record-sets --hosted-zone-id Z39E8QFU0F9PZP --change-batch file://r53-fluffychat.json
{
    "ChangeInfo": {
        "Id": "/change/C0880875M09JKJ8EH63E",
        "Status": "PENDING",
        "SubmittedAt": "2024-01-03T14:25:32.490Z",
        "Comment": "CREATE fluffychat fb.s A record "
    }
}

Let’s then create the Kubernetes passthrough that will sort out Certs

$ cat ingressfluffy.yaml
---
apiVersion: v1
kind: Endpoints
metadata:
  name: fluffychat-external-ip
subsets:
- addresses:
  - ip: 192.168.1.100
  ports:
  - name: fluffychat
    port: 9095
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: fluffychat-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: fluffychat
    port: 80
    protocol: TCP
    targetPort: 9095
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: fluffychat-external-ip
  generation: 1
  labels:
    app.kubernetes.io/instance: fluffychatingress
  name: fluffychatingress
spec:
  rules:
  - host: fluffychat.freshbrewed.science
    http:
      paths:
      - backend:
          service:
            name: fluffychat-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - fluffychat.freshbrewed.science
    secretName: fluffychat-tls

$ kubectl apply -f ingressfluffy.yaml
endpoints/fluffychat-external-ip created
service/fluffychat-external-ip created
ingress.networking.k8s.io/fluffychatingress created

And we can see it live now at https://fluffychat.freshbrewed.science/#/home

/content/images/2024/01/cinny-16.png

Mobile app

The mobile app is free on the Play store.

We can see won’t show prior encrypted messages

/content/images/2024/01/Screenshot_20240103_085815.jpg

It doesn’t have the same navigation. Just the chats you have been in lately. This is a deal breaker for me.

/content/images/2024/01/Screenshot_20240103_085804.jpg

It has the idea of “Stories”, whatever those are and can show you “Public Rooms” which seemed a bit shady to me.

/content/images/2024/01/Screenshot_20240103_085810.jpg

Summary

Both Cinny and Fluffychat are compelling options. I likely wouldn’t stick with Cinny just because it lacks calling. For me, that’s a requirement.

I didn’t cover the mobile app in detail as it’s pretty basic. The element app on Android is definitely superior.

Kubernetes Container Docker Matrix Cinny Fluffychat

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes