app/matrix: appservice-irc
diff --git a/app/matrix/prod.jsonnet b/app/matrix/prod.jsonnet
index e392bfb..baa48e4 100644
--- a/app/matrix/prod.jsonnet
+++ b/app/matrix/prod.jsonnet
@@ -1,6 +1,7 @@
 # matrix.hackerspace.pl, a matrix/synapse instance
 # This needs a secret provisioned, create with:
 #    kubectl -n matrix create secret generic synapse --from-literal=postgres_password=$(pwgen 24 1)
+#    kubectl -n matrix create secret generic appservice-irc-freenode-registration --from-file=registration.yaml=<(kubectl logs -n matrix $(kubectl get pods -n matrix --selector=job-name=appservice-irc-freenode-bootstrap --output=jsonpath='{.items[*].metadata.name}') | tail -n +4)
 
 local kube = import "../../kube/kube.libsonnet";
 local postgres = import "../../kube/postgres.libsonnet";
@@ -10,11 +11,12 @@
     local cfg = app.cfg,
     cfg:: {
         namespace: "matrix",
-        image: "matrixdotorg/synapse:v0.99.3.2",
-        riotImage: "bubuntux/riot-web:v1.1.0",
         domain: "matrix.hackerspace.pl",
         serverName: "hackerspace.pl",
         storageClassName: "waw-hdd-redundant-1",
+
+        synapseImage: "matrixdotorg/synapse:v0.99.4",
+        riotImage: "bubuntux/riot-web:v1.1.0",
     },
 
     metadata(component):: {
@@ -51,7 +53,7 @@
         },
     },
 
-    deployment: kube.Deployment("synapse") {
+    synapseDeployment: kube.Deployment("synapse") {
         metadata+: app.metadata("synapse"),
         spec+: {
             replicas: 1,
@@ -59,10 +61,13 @@
                 spec+: {
                     volumes_: {
                         data: kube.PersistentVolumeClaimVolume(app.dataVolume),
+                    } + {
+                        [k]: { secret: { secretName: "appservice-%s-registration" % [k] } }
+                        for k in std.objectFields(app.appservices)
                     },
                     containers_: {
                         web: kube.Container("synapse") {
-                            image: cfg.image,
+                            image: cfg.synapseImage,
                             ports_: {
                                 http: { containerPort: 8008 },
                             },
@@ -80,6 +85,9 @@
                             },
                             volumeMounts_: {
                                 data: { mountPath: "/data" },
+                            } + {
+                                [k]: { mountPath: "/appservices/%s" % [k] }
+                                for k in std.objectFields(app.appservices)
                             },
                         },
                     },
@@ -88,9 +96,9 @@
         },
     },
 
-    svc: kube.Service("synapse") {
+    synapseSvc: kube.Service("synapse") {
         metadata+: app.metadata("synapse"),
-        target_pod:: app.deployment.spec.template,
+        target_pod:: app.synapseDeployment.spec.template,
     },
 
     riotConfig: kube.ConfigMap("riot-web-config") {
@@ -138,7 +146,7 @@
                         config: kube.ConfigMapVolume(app.riotConfig),
                     },
                     containers_: {
-                        web: kube.Container("synapse") {
+                        web: kube.Container("riot-web") {
                             image: cfg.riotImage,
                             ports_: {
                                 http: { containerPort: 80 },
@@ -161,8 +169,22 @@
         target_pod:: app.riotDeployment.spec.template,
     },
 
-    ingress: kube.Ingress("synapse") {
-        metadata+: app.metadata("synapse") {
+    appservices: {
+        "irc-freenode": app.AppServiceIrc("freenode") {
+            cfg+: {
+                metadata: app.metadata("appservice-irc-freenode"),
+                config+: {
+                    homeserver+: {
+                        url: "https://%s" % [cfg.domain],
+                        domain: "%s" % [cfg.serverName],
+                    },
+                },
+            },
+        },
+    },
+
+    ingress: kube.Ingress("matrix") {
+        metadata+: app.metadata("matrix") {
             annotations+: {
                 "kubernetes.io/tls-acme": "true",
                 "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
@@ -182,11 +204,103 @@
                     http: {
                         paths: [
                             { path: "/", backend: app.riotSvc.name_port },
-                            { path: "/_matrix", backend: app.svc.name_port },
+                            { path: "/_matrix", backend: app.synapseSvc.name_port },
                         ]
                     },
                 }
             ],
         },
     },
+
+    AppServiceIrc(name):: {
+        local bridge = self,
+        local cfg = bridge.cfg,
+        cfg:: {
+            image: "registry.k0.hswaw.net/informatic/matrix-appservice-irc:0.11.2",
+            metadata: {},
+            config: std.native("parseYaml")(importstr "appservice-irc.yaml")[0],
+            storageClassName: "waw-hdd-redundant-1",
+        },
+
+        config: kube.ConfigMap("appservice-irc-%s" % [name]) {
+            metadata+: cfg.metadata,
+            data: {
+                "config.yaml": std.manifestJsonEx(cfg.config, ""),
+            },
+        },
+
+        dataVolume: kube.PersistentVolumeClaim("appservice-irc-%s" % [name]) {
+            metadata+: cfg.metadata,
+            spec+: {
+                storageClassName: cfg.storageClassName,
+                accessModes: [ "ReadWriteOnce" ],
+                resources: {
+                    requests: {
+                        storage: "10Gi",
+                    },
+                },
+            },
+        },
+
+        bootstrapJob: kube.Job("appservice-irc-%s-bootstrap" % [name]) {
+            metadata+: cfg.metadata {
+                labels: {
+                    "job-name": "appservice-irc-%s-bootstrap" % [name],
+                },
+            },
+            spec+: {
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.ConfigMapVolume(bridge.config),
+                        },
+                        containers_: {
+                            bootstrap: kube.Container("appservice-irc-%s-bootstrap" % [name]) {
+                                image: cfg.image,
+                                command: ["sh", "-c", "matrix-appservice-irc -r -u http://appservice-irc-%s:9999 -c /config/config.yaml -f /tmp/registration.yaml && cat /tmp/registration.yaml" % [name]],
+                                volumeMounts_: {
+                                    config: { mountPath: "/config" },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+
+        deployment: kube.Deployment("appservice-irc-%s" % [name]) {
+            metadata+: cfg.metadata,
+            spec+: {
+                replicas: 1,
+                template+: {
+                    spec+: {
+                        volumes_: {
+                            config: kube.ConfigMapVolume(bridge.config),
+                            data: kube.PersistentVolumeClaimVolume(bridge.dataVolume),
+                            registration: { secret: { secretName: "appservice-irc-%s-registration" % [name] } },
+                        },
+                        containers_: {
+                            appserviceIrc: kube.Container("appservice-irc-%s" % [name]) {
+                                image: cfg.image,
+                                command: ["matrix-appservice-irc", "-c", "/config/config.yaml", "-f", "/registration/registration.yaml", "-p", "9999"],
+                                ports_: {
+                                    http: { containerPort: 9999 },
+                                },
+                                volumeMounts_: {
+                                    registration: { mountPath: "/registration", },
+                                    config: { mountPath: "/config", },
+                                    data: { mountPath: "/data" },
+                                },
+                            },
+                        },
+                    },
+                },
+            },
+        },
+
+        svc: kube.Service("appservice-irc-%s" % [name]) {
+            metadata+: cfg.metadata,
+            target_pod:: bridge.deployment.spec.template,
+        },
+    },
 }