Initial version
This commit is contained in:
commit
5bc23daf4b
5
COPYING
Normal file
5
COPYING
Normal file
|
@ -0,0 +1,5 @@
|
|||
Copyright 2023 Erin Shepherd
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
16
Dockerfile
Normal file
16
Dockerfile
Normal file
|
@ -0,0 +1,16 @@
|
|||
ARG ALPINE=3.18
|
||||
FROM golang:1.21-alpine${ALPINE} AS build
|
||||
WORKDIR /app
|
||||
COPY go.mod go.sum .
|
||||
RUN go mod download
|
||||
|
||||
ADD . /app
|
||||
RUN go build
|
||||
|
||||
FROM alpine:${ALPINE}
|
||||
|
||||
WORKDIR /bin
|
||||
COPY --from=build /app/matrix-vanity .
|
||||
|
||||
WORKDIR /
|
||||
CMD ["/bin/matrix-vanity"]
|
50
README.md
Normal file
50
README.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Matrix-Vanity - Vanity names for Matrix rooms
|
||||
|
||||
This is an absolute bare minimum Matrix server implementation designed
|
||||
to do one thing and one thing only: Enable you to give rooms vanity names
|
||||
without running a homeserver
|
||||
|
||||
It handles just the room lookup by name endpoint, which is all that is needed
|
||||
for this purpose. Perhaps in the future it will implement the room directory
|
||||
as well, but for now it does not.
|
||||
|
||||
## Installation
|
||||
Pick a hostname and port that you will run this on. Organize for matrix-vanity
|
||||
to run behind a TLS terminating proxy (it does not handle TLS itself)
|
||||
|
||||
Create a JSON file defining rooms:
|
||||
```js
|
||||
{
|
||||
"#matrix:example.com": { // <-- Alias
|
||||
"room_id": "!OGEhHVWSdvArJzumhm:matrix.org", // Room ID, from the Advanced tab of the room settings in Element
|
||||
"servers": ["matrix.org"] // Pick one or more of the homeservers which host the room
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
When starting the server, set the following environment variables:
|
||||
|
||||
* `MXV_ROOMS_FILE` to the path to your room definition file,
|
||||
* `MXV_USE_PROXY_HEADERS` if you wish to have the service interpret X-Forwarded-For
|
||||
(only used for log output)
|
||||
* `MXV_LISTEN` to your preferred listening spec (Default: `:3333`)
|
||||
|
||||
Check things are running, and that you have TLS certificates configured.
|
||||
|
||||
Delegate Matrix server-to-server handling for your hostname to this instance
|
||||
[per the spec](https://spec.matrix.org/v1.8/server-server-api/#resolving-server-names),
|
||||
by setting up `/.well-known/matrix/server` (or picking one of the other methods if you
|
||||
prefer)
|
||||
|
||||
```json
|
||||
{
|
||||
"m.server": "matrix-vanity.example.com:443"
|
||||
}
|
||||
```
|
||||
(Remember that if you don't specify a port, the default is 8448)
|
||||
|
||||
Now for each of your rooms for which you wish to advertise this alias, go to the room's
|
||||
Visibility settings page in Element and enter the address in the "Other published addresses"
|
||||
field. If all is configured properly, it should turn green. You can then make it the main
|
||||
address if you wish.
|
9
go.mod
Normal file
9
go.mod
Normal file
|
@ -0,0 +1,9 @@
|
|||
module git.alioth.systems/erin/matrix-vanity
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.10 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
)
|
6
go.sum
Normal file
6
go.sum
Normal file
|
@ -0,0 +1,6 @@
|
|||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
123
main.go
Normal file
123
main.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/gorilla/handlers"
|
||||
)
|
||||
|
||||
type mxError struct {
|
||||
ErrCode string `json:"errcode"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
var internalErrorBuf = []byte(`{"errcode":"M_UNKNOWN", "error": "Internal error"}`)
|
||||
|
||||
type RoomMapping struct {
|
||||
RoomID string `json:"room_id"`
|
||||
Servers []string `json:"servers"`
|
||||
}
|
||||
|
||||
var KnownRooms = map[string]RoomMapping{}
|
||||
|
||||
func SendJSON(w http.ResponseWriter, obj any, status int) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
buf, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
log.Printf("Error marshaling JSON response: %v", err)
|
||||
w.WriteHeader(500)
|
||||
_, _ = w.Write(internalErrorBuf)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(status)
|
||||
_, _ = w.Write(buf)
|
||||
}
|
||||
|
||||
func NotFound(w http.ResponseWriter, r *http.Request) {
|
||||
SendJSON(w, mxError{"M_UNRECOGNIZED", "Unsupported Endpoint"}, 404)
|
||||
}
|
||||
|
||||
func MethodNotAllowed(w http.ResponseWriter, r *http.Request) {
|
||||
SendJSON(w, mxError{"M_UNRECOGNIZED", "Unrecognized Method"}, 405)
|
||||
}
|
||||
|
||||
func Version(w http.ResponseWriter, r *http.Request) {
|
||||
SendJSON(w, map[string]any{
|
||||
"name": "Matrix-Vanity",
|
||||
"version": "0.0",
|
||||
}, 200)
|
||||
}
|
||||
|
||||
func QueryDirectory(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
roomAlias := q.Get("room_alias")
|
||||
|
||||
mapping, ok := KnownRooms[roomAlias]
|
||||
if !ok {
|
||||
log.Printf("Room '%s' not found", roomAlias)
|
||||
SendJSON(w, mxError{"M_NOT_FOUND", "Room alias not found."}, 404)
|
||||
return
|
||||
}
|
||||
|
||||
SendJSON(w, mapping, 200)
|
||||
}
|
||||
|
||||
func Index(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte("This is a Matrix-Vanity server"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
useProxyIP := os.Getenv("MXV_USE_PROXY_HEADERS") != ""
|
||||
|
||||
configFile := os.Getenv("MXV_ROOMS_FILE")
|
||||
if configFile == "" {
|
||||
configFile = "/etc/matrix-vanity-rooms.json"
|
||||
}
|
||||
log.Printf("Loading room list from %s", configFile)
|
||||
|
||||
listenAddr := os.Getenv("MXV_LISTEN")
|
||||
if listenAddr == "" {
|
||||
listenAddr = ":3333"
|
||||
}
|
||||
|
||||
buf, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading room file: %v", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(buf, &KnownRooms); err != nil {
|
||||
log.Fatalf("Error parsing room file: %v", err)
|
||||
}
|
||||
|
||||
log.Print("Defined Rooms:")
|
||||
for alias, info := range KnownRooms {
|
||||
log.Printf(" - '%s', ID: '%s', Servers: %v", alias, info.RoomID, info.Servers)
|
||||
}
|
||||
if len(KnownRooms) == 0 {
|
||||
log.Printf("⚠️ No rooms defined. That doesn't seem very useful ")
|
||||
}
|
||||
log.Print("")
|
||||
|
||||
r := chi.NewRouter()
|
||||
if useProxyIP {
|
||||
log.Print("X-Forwarded-For header intepretation enabled; it is assumed you are running this service behind a proxy")
|
||||
r.Use(handlers.ProxyHeaders)
|
||||
}
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
r.NotFound(NotFound)
|
||||
r.MethodNotAllowed(MethodNotAllowed)
|
||||
r.Get("/", Index)
|
||||
r.Get("/_matrix/federation/v1/version", Version)
|
||||
r.Get("/_matrix/federation/v1/query/directory", QueryDirectory)
|
||||
log.Printf("Listening on %q", listenAddr)
|
||||
http.ListenAndServe(listenAddr, r)
|
||||
}
|
Loading…
Reference in a new issue