diff --git a/package.json b/package.json
index 586b537e87ebd32f95a13816824cbc974a6db78f..28b1609df84a81127e2d3320ad550a9a89c2058b 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,6 @@
         "@quentin-sommer/react-useragent": "^3.1.0",
         "classnames": "^2.2.6",
         "formik": "^2.1.4",
-        "matrix-cypher": "^0.1.12",
         "react": "^16.13.1",
         "react-dom": "^16.13.1",
         "react-scripts": "3.4.1",
diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx
index 00c518322c4eaf5b581195e5bab31677bc2b8b8c..f150c6333e1d2998919cf8f3e58ad5fb3230c902 100644
--- a/src/components/Avatar.tsx
+++ b/src/components/Avatar.tsx
@@ -16,7 +16,7 @@ limitations under the License.
 
 import React, { useEffect, useState } from 'react';
 import classNames from 'classnames';
-import { Room, User } from 'matrix-cypher';
+import { Room, User } from '../matrix-cypher';
 
 import { getMediaQueryFromMCX } from '../utils/cypher-wrapper';
 import logo from '../imgs/chat-icon.svg';
diff --git a/src/components/EventPreview.tsx b/src/components/EventPreview.tsx
index de7ac7f019c63b10a3a7ea75bd761e56d829730f..00ac979efe0e6903cc40e1ab432b83115ebab953 100644
--- a/src/components/EventPreview.tsx
+++ b/src/components/EventPreview.tsx
@@ -15,7 +15,7 @@ limitations under the License.
 */
 
 import React from 'react';
-import { Room, Event } from 'matrix-cypher';
+import { Room, Event } from '../matrix-cypher';
 
 import RoomPreview from './RoomPreview';
 
diff --git a/src/components/LinkPreview.tsx b/src/components/LinkPreview.tsx
index 58c0fff636df1485441b7574582fcf48457d9407..ae4170c8e4462624017c1d02e1b3aa3b624bf496 100644
--- a/src/components/LinkPreview.tsx
+++ b/src/components/LinkPreview.tsx
@@ -15,7 +15,7 @@ limitations under the License.
 */
 
 import React, { useState, useEffect, useContext } from 'react';
-import { getEvent, client } from 'matrix-cypher';
+import { getEvent, client } from '../matrix-cypher';
 
 import { RoomPreviewWithTopic } from './RoomPreview';
 import InviteTile from './InviteTile';
diff --git a/src/components/RoomPreview.tsx b/src/components/RoomPreview.tsx
index 2874f2b7c6d6b3804a441fd8b959d8ac9ab58ab7..0ef2f14c61043d17326815267ef678b856a61647 100644
--- a/src/components/RoomPreview.tsx
+++ b/src/components/RoomPreview.tsx
@@ -15,7 +15,7 @@ limitations under the License.
 */
 
 import React from 'react';
-import { Room } from 'matrix-cypher';
+import { Room } from '../matrix-cypher';
 
 import { RoomAvatar } from './Avatar';
 
diff --git a/src/components/UserPreview.tsx b/src/components/UserPreview.tsx
index b1a38ee07fd9d3cfd45cb056ef439d2e90a745e8..f0928c875553340a29b01d8d35f94682177d9442 100644
--- a/src/components/UserPreview.tsx
+++ b/src/components/UserPreview.tsx
@@ -15,7 +15,7 @@ limitations under the License.
 */
 
 import React, { useState, useEffect } from 'react';
-import { client, User, getUserDetails } from 'matrix-cypher';
+import { client, User, getUserDetails } from '../matrix-cypher';
 import classNames from 'classnames';
 import icon from '../imgs/chat-icon.svg';
 
diff --git a/src/contexts/HSContext.ts b/src/contexts/HSContext.ts
index 1203f3f6233189cdc241e12a63b6d11b3d545580..4105af697fa290f9a884a6ecd4c8d20e01602c14 100644
--- a/src/contexts/HSContext.ts
+++ b/src/contexts/HSContext.ts
@@ -19,8 +19,6 @@ import { string, object, union, literal, TypeOf } from 'zod';
 
 import { persistReducer } from '../utils/localStorage';
 
-//import { prefixFetch, Client, discoverServer } from 'matrix-cypher';
-
 export enum HSOptions {
     // The homeserver contact policy hasn't
     // been set yet.
diff --git a/src/matrix-cypher/index.ts b/src/matrix-cypher/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8bdc9331def67e4df947a21300bba65a5c55ef39
--- /dev/null
+++ b/src/matrix-cypher/index.ts
@@ -0,0 +1,19 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+export * from './matrix-cypher';
+export * from './utils';
+export * from './schemas';
diff --git a/src/matrix-cypher/matrix-cypher.ts b/src/matrix-cypher/matrix-cypher.ts
new file mode 100644
index 0000000000000000000000000000000000000000..75387a2047fcf749dd44b0f532395fedaf91871d
--- /dev/null
+++ b/src/matrix-cypher/matrix-cypher.ts
@@ -0,0 +1,193 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an 'AS IS' BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/* eslint-disable import/first */
+
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore
+import any from 'promise.any';
+any.shim()
+
+import VersionSchema from './schemas/VersionSchema';
+import WellKnownSchema from './schemas/WellKnownSchema';
+import UserSchema, { User } from './schemas/UserSchema';
+import RoomAliasSchema, {
+    RoomAlias,
+} from './schemas/RoomAliasSchema';
+import PublicRoomsSchema, {
+    PublicRooms,
+    Room,
+} from './schemas/PublicRoomsSchema';
+import EventSchema, {
+    Event,
+} from './schemas/EventSchema';
+import { ensure } from './utils/promises';
+import { prefixFetch, parseJSON } from './utils/fetch';
+
+
+/*
+ * A client is a resolved homeserver name wrapped in a lambda'd fetch
+ */
+export type Client = (path: string) => Promise<Response>;
+
+/*
+ * Confirms that the target homeserver is properly configured and operational
+ */
+export const validateHS = (host: string) =>
+    prefixFetch(host)('/_matrix/client/versions')
+        .then(parseJSON)
+        .then(VersionSchema.parse)
+        .then(() => host);
+
+/*
+ * Discovers the correct domain name for the host according to the spec's
+ * discovery rules
+ */
+export const discoverServer = (host: string) =>
+    prefixFetch(host)('/.well-known/matrix/client')
+        .then(resp => resp.ok
+            ? resp.json()
+                .then(WellKnownSchema.parse)
+                .then(content => {
+                    if (content === undefined) return host;
+                    else if (
+                        'm.homeserver' in content && content['m.homeserver']
+                    ) {
+                        return content['m.homeserver'].base_url
+                    } else {
+                        return host
+                    }
+                })
+            : ensure(
+                resp.status === 404,
+                () => host,
+            ),
+        )
+        .then(validateHS)
+
+
+/*
+ * Takes a hs domain and resolves it to it's current domain and returns a
+ * client
+ */
+export async function client(host: string): Promise<Client> {
+    return prefixFetch(await discoverServer(host))
+}
+
+/*
+ * Gets the details for a user
+ */
+export function getUserDetails(
+    client: Client,
+    userId: string,
+): Promise<User> {
+    return client(`/_matrix/client/r0/profile/${userId}`)
+        .then(parseJSON)
+        .then(UserSchema.parse)
+}
+
+/*
+ * Gets the roomId of a room by resolving it's alias
+ */
+export function getRoomIdFromAlias(
+    client: Client,
+    roomAlias: string,
+): Promise<RoomAlias> {
+    const encodedRoomAlias = encodeURIComponent(roomAlias);
+    return client(`/_matrix/client/r0/directory/room/${encodedRoomAlias}`)
+        .then(parseJSON)
+        .then(RoomAliasSchema.parse);
+}
+
+/*
+ * Gets the details of a room if that room is public
+ */
+export function getRoomDetails(clients: Client[], roomId: string): Promise<Room> {
+    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+    // @ts-ignore
+    return Promise.any(clients.map(client => searchPublicRooms(client, roomId)));
+}
+
+/*
+ * Gets a list of all public rooms on a hs
+ */
+export function getPublicRooms(client: Client): Promise<PublicRooms> {
+    return getPublicRoomsUnsafe(client)
+        .then(PublicRoomsSchema.parse)
+}
+
+/*
+ * Similar to getPubliRooms however id doesn't confirm the data returned from
+ * the hs is correct
+ *
+ * This is used because the room list can be huge and validating it all takes
+ * a long time
+ */
+export function getPublicRoomsUnsafe(client: Client): Promise<PublicRooms> {
+    // TODO: Do not assume server will return all results in one go
+    return client('/_matrix/client/r0/publicRooms')
+        .then(parseJSON)
+}
+
+/*
+ * Searches the public rooms of a homeserver for the metadata of a particular
+ */
+export function searchPublicRooms(
+    client: Client,
+    roomId: string,
+): Promise<Room> {
+    // we use the unsage version here because the safe one is sloooow
+    return getPublicRoomsUnsafe(client)
+        .then(rooms => {
+            const [match] = rooms.chunk.filter(
+                chunk => chunk.room_id === roomId,
+            );
+            return match !== undefined
+                ? Promise.resolve(match)
+                : Promise.reject(new Error(
+                    `This server knowns no public room with id ${roomId}`,
+                ));
+        });
+}
+
+/*
+ * Gets the details of an event from the homeserver
+ */
+export async function getEvent(
+    client: Client,
+    roomIdOrAlias: string,
+    eventId: string,
+): Promise<Event> {
+    return client(`/_matrix/client/r0/rooms/${roomIdOrAlias}/event/${eventId}`)
+        .then(parseJSON)
+        .then(EventSchema.parse);
+}
+
+/*
+ * Gets an mxc resource
+ */
+export function convertMXCtoMediaQuery(
+    clientURL: string,
+    mxc: string,
+): string {
+    // mxc://matrix.org/EqMZYbAYhREvHXvYFyfxOlkf
+    const matches = mxc.match(/mxc:\/\/(.+)\/(.+)/)
+    if (!matches) {
+        throw new Error(`mxc invalid: ${JSON.stringify({mxc})}`);
+    }
+
+    return `${clientURL}/_matrix/media/r0/download/${matches[1]}/${matches[2]}`;
+}
diff --git a/src/matrix-cypher/schemas/EventSchema.ts b/src/matrix-cypher/schemas/EventSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b0402abcf47acadaba5e61a2a601d6ddc4b7f31
--- /dev/null
+++ b/src/matrix-cypher/schemas/EventSchema.ts
@@ -0,0 +1,30 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { object, string, TypeOf } from 'zod';
+
+const EventSchema = object({
+  content: object({}).nonstrict(),
+  type: string(),
+  event_id: string(),
+  sender: string(),
+  origin_server_ts: string(),
+  unsigned: object({}).nonstrict().optional(),
+  room_id: string(),
+});
+
+export type Event = TypeOf<typeof EventSchema>;
+export default EventSchema;
diff --git a/src/matrix-cypher/schemas/PublicRoomsSchema.ts b/src/matrix-cypher/schemas/PublicRoomsSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b33cb31cc63bc32fde32fafc78b709f102e69544
--- /dev/null
+++ b/src/matrix-cypher/schemas/PublicRoomsSchema.ts
@@ -0,0 +1,43 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { object, array, string, boolean, number, TypeOf } from 'zod';
+
+export const RoomSchema = object({
+  aliases: array(string()).optional(),
+  canonical_alias: string().optional(),
+  name: string().optional(),
+  num_joined_members: number(),
+  room_id: string(),
+  topic: string().optional(),
+  world_readable: boolean(),
+  guest_can_join: boolean(),
+  avatar_url: string().optional(),
+});
+
+
+const PublicRoomsSchema = object({
+  chunk: array(RoomSchema),
+  next_batch: string().optional(),
+  prev_batch: string().optional(),
+  total_room_count_estimate: number().optional(),
+});
+
+export type Room = TypeOf<typeof RoomSchema>;
+export type PublicRooms = TypeOf<typeof PublicRoomsSchema>;
+
+export default PublicRoomsSchema;
+
diff --git a/src/matrix-cypher/schemas/RoomAliasSchema.ts b/src/matrix-cypher/schemas/RoomAliasSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f662a4b3c04e6c1e4620d079c2fee0102b6189df
--- /dev/null
+++ b/src/matrix-cypher/schemas/RoomAliasSchema.ts
@@ -0,0 +1,26 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { object, array, string, TypeOf } from 'zod';
+
+const RoomAliasSchema = object({
+  room_id: string(),
+  servers: array(string()),
+});
+
+export type RoomAlias = TypeOf<typeof RoomAliasSchema>;
+export default RoomAliasSchema;
+
diff --git a/src/matrix-cypher/schemas/UserSchema.ts b/src/matrix-cypher/schemas/UserSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4702e8ea98a40eb5f02b8bd5218164779454e8b0
--- /dev/null
+++ b/src/matrix-cypher/schemas/UserSchema.ts
@@ -0,0 +1,26 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { object, string, TypeOf } from 'zod';
+
+const UserSchema = object({
+  avatar_url: string().optional(),
+  displayname: string().optional(),
+})
+
+export type User = TypeOf<typeof UserSchema>;
+export default UserSchema;
+
diff --git a/src/matrix-cypher/schemas/VersionSchema.ts b/src/matrix-cypher/schemas/VersionSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3af08c0ec0708cee0c5131f6fc7649cd35f44c30
--- /dev/null
+++ b/src/matrix-cypher/schemas/VersionSchema.ts
@@ -0,0 +1,21 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { object, string, array } from 'zod';
+
+export default object({
+  versions: array(string()),
+}).nonstrict()
diff --git a/src/matrix-cypher/schemas/WellKnownSchema.ts b/src/matrix-cypher/schemas/WellKnownSchema.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7cfaa29e831b6d1b2985ee0ccaa1d03d7b25fa1f
--- /dev/null
+++ b/src/matrix-cypher/schemas/WellKnownSchema.ts
@@ -0,0 +1,29 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { object, string, TypeOf } from 'zod';
+
+const WellKnownSchema = object({
+  'm.homeserver': object({
+    'base_url': string().url(),
+  }),
+  'm.identity_server': object({
+    'base_url': string().url(),
+  }),
+});
+
+export type WellKnown = TypeOf<typeof WellKnownSchema>;
+export default WellKnownSchema;
diff --git a/src/matrix-cypher/schemas/index.ts b/src/matrix-cypher/schemas/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f7fa3c32a40b8a0ab33f28522c4178fda604680b
--- /dev/null
+++ b/src/matrix-cypher/schemas/index.ts
@@ -0,0 +1,24 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+export * from './EventSchema';
+export * from './PublicRoomsSchema';
+export * from './RoomAliasSchema';
+export * from './UserSchema';
+export * from './VersionSchema';
+export * from './WellKnownSchema';
+export * from './index';
+
diff --git a/src/matrix-cypher/utils/fetch.ts b/src/matrix-cypher/utils/fetch.ts
new file mode 100644
index 0000000000000000000000000000000000000000..44255fb0a0ce1f7c79c644408a65d80d7bbf0e79
--- /dev/null
+++ b/src/matrix-cypher/utils/fetch.ts
@@ -0,0 +1,36 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import fetch from 'cross-fetch';
+
+import { ensure } from './promises';
+
+/*
+ * Wraps a fetch with a domain for easy reuse.
+ */
+export function prefixFetch(host: string) {
+    return (path: string) => fetch(
+        new URL(path, host).toString(),
+    );
+}
+
+export function parseJSON(resp: Response) {
+    return ensure(
+        resp.ok,
+        () => resp.json(),
+        `Error from Homeserver. Error code: ${resp.status}`,
+    );
+}
diff --git a/src/matrix-cypher/utils/index.ts b/src/matrix-cypher/utils/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4e4adb1b9f3e750539e1102a89c8a5b53dd64a18
--- /dev/null
+++ b/src/matrix-cypher/utils/index.ts
@@ -0,0 +1,18 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+export * from './fetch';
+export * from './promises';
diff --git a/src/matrix-cypher/utils/promises.ts b/src/matrix-cypher/utils/promises.ts
new file mode 100644
index 0000000000000000000000000000000000000000..08ee36bceb606060d33f432a053d56746d3feee7
--- /dev/null
+++ b/src/matrix-cypher/utils/promises.ts
@@ -0,0 +1,60 @@
+/*
+Copyright 2020 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+/*
+ * Conditional promises
+ */
+
+/*
+ * If the condition is false reject with rejectReason
+ * If it's true resolve with the result = resultThunk()
+ */
+export function ensure<T>(condition: boolean, resultThunk: () => T | PromiseLike<T>, rejectReason?: string) {
+    return condition
+        ? Promise.resolve(resultThunk())
+        : Promise.reject(new Error(rejectReason));
+}
+
+/*
+ * Loggin utilities
+ */
+
+/*
+ * Logs a then using "success: {label: successArg}"
+ */
+export function logThen<T>(label: string): (v: T) => T | PromiseLike<T> {
+    return (v: T) => {
+        console.log('success:', {[`${label}`]: v}); return v
+    }
+}
+
+/*
+ * Logs a catch using "fail: {label: failArg}"
+ */
+export function logCatch<T>(label: string): (v: T) => T | PromiseLike<T> {
+    return (v: T) => {
+        console.log('fail:', {[`${label}`]: v});
+        return Promise.reject(v)
+    }
+}
+
+/*
+ * inserts loggers for both callbacks of a then
+ */
+export function logThens<T1, T2 = T1>(label: string) {
+    return [logThen<T1>(label), logCatch<T2>(label)]
+}
+
diff --git a/src/utils/cypher-wrapper.ts b/src/utils/cypher-wrapper.ts
index 5155d20c590cc938325b6e6a9cfe5d6574bf5589..7e1093eedd09ab4f8c3ee7b6bcfa95cd5a98b189 100644
--- a/src/utils/cypher-wrapper.ts
+++ b/src/utils/cypher-wrapper.ts
@@ -28,7 +28,7 @@ import {
     searchPublicRooms,
     getUserDetails,
     convertMXCtoMediaQuery,
-} from 'matrix-cypher';
+} from '../matrix-cypher';
 import { LinkKind, Permalink } from '../parser/types';
 
 /* This is a collection of methods for providing fallback metadata