diff --git a/.env.example b/.env.example
index 932846bfe..169e6db0d 100644
--- a/.env.example
+++ b/.env.example
@@ -1,13 +1,13 @@
# Keys
# Required key for platform encryption/decryption ops
-ENCRYPTION_KEY=replace_with_lengthy_secure_hex
+ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218
# JWT
# Required secrets to sign JWT tokens
-JWT_SIGNUP_SECRET=replace_with_lengthy_secure_hex
-JWT_REFRESH_SECRET=replace_with_lengthy_secure_hex
-JWT_AUTH_SECRET=replace_with_lengthy_secure_hex
-JWT_SERVICE_SECRET=replace_with_lengthy_secure_hex
+JWT_SIGNUP_SECRET=3679e04ca949f914c03332aaaeba805a
+JWT_REFRESH_SECRET=5f2f3c8f0159068dc2bbb3a652a716ff
+JWT_AUTH_SECRET=4be6ba5602e0fa0ac6ac05c3cd4d247f
+JWT_SERVICE_SECRET=f32f716d70a42c5703f4656015e76200
# JWT lifetime
# Optional lifetimes for JWT tokens expressed in seconds or a string
@@ -34,15 +34,12 @@ MONGO_PASSWORD=example
SITE_URL=http://localhost:8080
# Mail/SMTP
-# Required to send emails
-# By default, SMTP_HOST is set to smtp.gmail.com, SMTP_PORT is set to 587, SMTP_TLS is set to false, and SMTP_FROM_NAME is set to Infisical
-SMTP_HOST=smtp.gmail.com
-# If STARTTLS is supported, the connection will be upgraded to TLS when SMTP_SECURE is set to false
-SMTP_SECURE=false
+SMTP_HOST= # required
+SMTP_USERNAME= # required
+SMTP_PASSWORD= # required
SMTP_PORT=587
-SMTP_USERNAME=
-SMTP_PASSWORD=
-SMTP_FROM_ADDRESS=
+SMTP_SECURE=false
+SMTP_FROM_ADDRESS= # required
SMTP_FROM_NAME=Infisical
# Integration
diff --git a/README.md b/README.md
index d86bcc05b..3851b3c53 100644
--- a/README.md
+++ b/README.md
@@ -321,4 +321,4 @@ Infisical officially launched as v.1.0 on November 21st, 2022. However, a lot of
-
+
diff --git a/backend/package-lock.json b/backend/package-lock.json
index 9020620dd..42c8205bd 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -31,7 +31,7 @@
"libsodium-wrappers": "^0.7.10",
"mongoose": "^6.7.2",
"nodemailer": "^6.8.0",
- "posthog-node": "^2.2.0",
+ "posthog-node": "^2.2.2",
"query-string": "^7.1.3",
"rimraf": "^3.0.2",
"stripe": "^10.7.0",
@@ -9810,9 +9810,9 @@
}
},
"node_modules/posthog-node": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.2.0.tgz",
- "integrity": "sha512-p5ltKQO6YvBNy09OmpJvcknvxBAs8MvHv1AGbXGWDdnGCwKZxa9Ln2cN5XHnzU7rYuNT7YMAVQzZ6cE7Mu1yPA==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.2.2.tgz",
+ "integrity": "sha512-aXYe/D+28kF63W8Cz53t09ypEORz+ULeDCahdAqhVrRm2scbOXFbtnn0GGhvMpYe45grepLKuwui9KxrZ2ZuMw==",
"dependencies": {
"axios": "^0.27.0"
},
@@ -18752,9 +18752,9 @@
}
},
"posthog-node": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.2.0.tgz",
- "integrity": "sha512-p5ltKQO6YvBNy09OmpJvcknvxBAs8MvHv1AGbXGWDdnGCwKZxa9Ln2cN5XHnzU7rYuNT7YMAVQzZ6cE7Mu1yPA==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.2.2.tgz",
+ "integrity": "sha512-aXYe/D+28kF63W8Cz53t09ypEORz+ULeDCahdAqhVrRm2scbOXFbtnn0GGhvMpYe45grepLKuwui9KxrZ2ZuMw==",
"requires": {
"axios": "^0.27.0"
},
diff --git a/backend/package.json b/backend/package.json
index 329e027b5..266bb7b39 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -22,7 +22,7 @@
"libsodium-wrappers": "^0.7.10",
"mongoose": "^6.7.2",
"nodemailer": "^6.8.0",
- "posthog-node": "^2.2.0",
+ "posthog-node": "^2.2.2",
"query-string": "^7.1.3",
"rimraf": "^3.0.2",
"stripe": "^10.7.0",
diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts
index 43a9026ac..08fb1862e 100644
--- a/backend/src/config/index.ts
+++ b/backend/src/config/index.ts
@@ -25,9 +25,9 @@ const POSTHOG_PROJECT_API_KEY =
'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
const SENTRY_DSN = process.env.SENTRY_DSN!;
const SITE_URL = process.env.SITE_URL!;
-const SMTP_HOST = process.env.SMTP_HOST! || 'smtp.gmail.com';
-const SMTP_SECURE = process.env.SMTP_SECURE! || false;
-const SMTP_PORT = process.env.SMTP_PORT! || 587;
+const SMTP_HOST = process.env.SMTP_HOST!;
+const SMTP_SECURE = process.env.SMTP_SECURE! === 'true' || false;
+const SMTP_PORT = parseInt(process.env.SMTP_PORT!) || 587;
const SMTP_USERNAME = process.env.SMTP_USERNAME!;
const SMTP_PASSWORD = process.env.SMTP_PASSWORD!;
const SMTP_FROM_ADDRESS = process.env.SMTP_FROM_ADDRESS!;
diff --git a/backend/src/integrations/exchange.ts b/backend/src/integrations/exchange.ts
index dafddc785..cb0ff84e0 100644
--- a/backend/src/integrations/exchange.ts
+++ b/backend/src/integrations/exchange.ts
@@ -9,8 +9,7 @@ import {
INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL,
- INTEGRATION_GITHUB_API_URL,
- ACTION_PUSH_TO_HEROKU
+ INTEGRATION_GITHUB_API_URL
} from '../variables';
import {
SITE_URL,
diff --git a/backend/src/services/smtp.ts b/backend/src/services/smtp.ts
index 14d543439..12841eee7 100644
--- a/backend/src/services/smtp.ts
+++ b/backend/src/services/smtp.ts
@@ -1,13 +1,14 @@
import nodemailer from 'nodemailer';
import { SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_SECURE } from '../config';
+import { SMTP_HOST_SENDGRID, SMTP_HOST_MAILGUN } from '../variables';
import SMTPConnection from 'nodemailer/lib/smtp-connection';
import * as Sentry from '@sentry/node';
const mailOpts: SMTPConnection.Options = {
host: SMTP_HOST,
- secure: SMTP_SECURE as boolean,
port: SMTP_PORT as number
};
+
if (SMTP_USERNAME && SMTP_PASSWORD) {
mailOpts.auth = {
user: SMTP_USERNAME,
@@ -15,6 +16,23 @@ if (SMTP_USERNAME && SMTP_PASSWORD) {
};
}
+if (SMTP_SECURE) {
+ switch (SMTP_HOST) {
+ case SMTP_HOST_SENDGRID:
+ mailOpts.requireTLS = true;
+ break;
+ case SMTP_HOST_MAILGUN:
+ mailOpts.requireTLS = true;
+ mailOpts.tls = {
+ ciphers: 'TLSv1.2'
+ }
+ break;
+ default:
+ mailOpts.secure = true;
+ break;
+ }
+}
+
export const initSmtp = () => {
const transporter = nodemailer.createTransport(mailOpts);
transporter
diff --git a/backend/src/variables/action.ts b/backend/src/variables/action.ts
deleted file mode 100644
index 1f913bbe9..000000000
--- a/backend/src/variables/action.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-const ACTION_PUSH_TO_HEROKU = 'pushToHeroku';
-
-export {
- ACTION_PUSH_TO_HEROKU
-}
\ No newline at end of file
diff --git a/backend/src/variables/index.ts b/backend/src/variables/index.ts
index c69ed8176..e284d6d5c 100644
--- a/backend/src/variables/index.ts
+++ b/backend/src/variables/index.ts
@@ -32,9 +32,9 @@ import {
GRANTED
} from './organization';
import { SECRET_SHARED, SECRET_PERSONAL } from './secret';
-import { PLAN_STARTER, PLAN_PRO } from './stripe';
import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event';
-import { ACTION_PUSH_TO_HEROKU } from './action';
+import { SMTP_HOST_SENDGRID, SMTP_HOST_MAILGUN } from './smtp';
+import { PLAN_STARTER, PLAN_PRO } from './stripe';
export {
OWNER,
@@ -44,8 +44,6 @@ export {
ACCEPTED,
COMPLETED,
GRANTED,
- PLAN_STARTER,
- PLAN_PRO,
SECRET_SHARED,
SECRET_PERSONAL,
ENV_DEV,
@@ -69,6 +67,9 @@ export {
INTEGRATION_GITHUB_API_URL,
EVENT_PUSH_SECRETS,
EVENT_PULL_SECRETS,
- ACTION_PUSH_TO_HEROKU,
- INTEGRATION_OPTIONS
+ INTEGRATION_OPTIONS,
+ SMTP_HOST_SENDGRID,
+ SMTP_HOST_MAILGUN,
+ PLAN_STARTER,
+ PLAN_PRO,
};
diff --git a/backend/src/variables/smtp.ts b/backend/src/variables/smtp.ts
new file mode 100644
index 000000000..4db7c9f12
--- /dev/null
+++ b/backend/src/variables/smtp.ts
@@ -0,0 +1,7 @@
+const SMTP_HOST_SENDGRID = 'smtp.sendgrid.net';
+const SMTP_HOST_MAILGUN = 'smtp.mailgun.org';
+
+export {
+ SMTP_HOST_SENDGRID,
+ SMTP_HOST_MAILGUN
+}
\ No newline at end of file
diff --git a/docs/contributing/FAQ.mdx b/docs/contributing/FAQ.mdx
deleted file mode 100644
index 9f4f7aee9..000000000
--- a/docs/contributing/FAQ.mdx
+++ /dev/null
@@ -1,18 +0,0 @@
----
-title: 'Frequently Asked Questions'
-description: 'Have any questions? [Join our Slack community](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g).'
----
-
-## Problem with SMTP
-
-If you opt for actual SMTP server (not the local MailHog), you have to have the right environment variables set.
-
-You can normally populate `SMTP_USERNAME` and `SMTP_PASSWORD` with your usual login and password (you could also create a 'burner' email). Sometimes, there still are problems.
-
-You can go to your Gmail account settings > security and enable “less secure apps”. This would allow Infisical to use your Gmail to send emails.
-
-If it still doesn't work, [this](https://stackoverflow.com/questions/72547853/unable-to-send-email-in-c-sharp-less-secure-app-access-not-longer-available/72553362#72553362) should help.
-
-## `MONGO_URL` issues
-
-Your `MONGO_URL` should be something like `mongodb://root:example@mongo:27017/?authSource=admin`. If you want to change it (not recommended), you should make sure that you keep this URL in line with `MONGO_USERNAME=root` and `MONGO_PASSWORD=example`.
diff --git a/docs/contributing/developing.mdx b/docs/contributing/developing.mdx
index 812025dbc..423b83da2 100644
--- a/docs/contributing/developing.mdx
+++ b/docs/contributing/developing.mdx
@@ -16,53 +16,43 @@ cd infisical
## Set up environment variables
-Start by creating a .env file at the root of the Infisical directory
+Start by creating a .env file at the root of the Infisical directory. It's best to start with the provided [`.env.example`](https://github.com/Infisical/infisical/blob/main/.env.example) template containing the necessary envars to fill out your .env file — you only have to modify the SMTP parameters.
+
+
+ The pre-populated environment variable values in the `.env.example` file are meant to be used in development only.
+ You'll want to fill in your own values in production, especially concerning encryption keys, secrets, and SMTP parameters.
+
+
+Refer to the [environment variable list](https://infisical.com/docs/self-hosting/configuration/envars) for guidance on each envar.
+
+### Helpful tips for developing with Infisical:
- Reference the [environment variable list](https://infisical.com/docs/self-hosting/configuration/envars) and provided [`.env.example`](https://raw.githubusercontent.com/Infisical/infisical/main/.env.example) template to fill out your .env file.
+Use the `ENCRYPTION_KEY`, JWT-secret envars, `MONGO_URL`, `MONGO_USERNAME`, `MONGO_PASSWORD` provided in the `.env.example` file.
+
+If setting your own values:
+
+- `ENCRYPTION_KEY` should be a [32-byte random hex](https://www.browserling.com/tools/random-hex)
+- `MONGO_URL` should take the form: `mongodb://[MONGO_USERNAME]:[MONGO_PASSWORD]@mongo:27017/?authSource=admin`.
-### Keys
+
+Bring and configure your own SMTP server by following our [email configuration guide](https://infisical.com/docs/self-hosting/configuration/email) (we recommend using either SendGrid or Mailgun).
-`ENCRYPTION_KEY`, `JWT_SIGNUP_SECRET`, `JWT_REFRESH_SECRET`, `JWT_AUTH_SECRET`, `JWT_SERVICE_SECRET` values can be generated with this [32-byte random hex generator](https://www.browserling.com/tools/random-hex).
-
-### Database
-
-Use to the following `MONGO_URL`, `MONGO_USERNAME`, `MONGO_PASSWORD`, `SITE_URL` values:
-
-```
-MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin
-MONGO_USERNAME=root
-MONGO_PASSWORD=example
-
-SITE_URL=http://localhost:8080
-```
-
-
- If you decide to use your own `MONGO_USERNAME` and `MONGO_PASSWORD`, you'll have to modify `MONGO_URL` to take the form: `mongodb://[MONGO_USERNAME]:[MONGO_PASSWORD]@mongo:27017/?authSource=admin`.
-
-
-### Mailing
-
-Option 1: Bring your own SMTP server and credentials by filling in `SMTP_HOST`, `SMTP_FROM_ADDRESS`, `SMTP_FROM_NAME`, `SMTP_USERNAME`, and `SMTP_PASSWORD`.
-
- `SMTP_HOST` is set to `smtp.gmail.com` by default. For `SMTP_USERNAME` and `SMTP_PASSWORD`, you'll need an email with 2-step-verification and an [app password](https://support.google.com/mail/answer/185833?hl=en) for it.
-
-
-
-Option 2: Use the provided (Mailhog) SMTP server and browse emails sent by the backend on `http://localhost:8025`. To use this option, set the following `SMTP_HOST`, `SMTP_PORT`, `SMTP_FROM_NAME`, `SMTP_USERNAME`, `SMTP_PASSWORD` values:
+Alternatively, you can use the provided development (Mailhog) SMTP server to send and browse emails sent by the backend on http://localhost:8025; to use this option, set the following `SMTP_HOST`, `SMTP_PORT`, `SMTP_FROM_NAME`, `SMTP_USERNAME`, `SMTP_PASSWORD` below.
+
```
SMTP_HOST=smtp-server
SMTP_PORT=1025
SMTP_FROM_ADDRESS=team@infisical.com
-SMTP_FROM_NAME=[whatever you like]
+SMTP_FROM_NAME=Infisical
SMTP_USERNAME=team@infisical.com
SMTP_PASSWORD=
```
- Make sure to leave the `SMTP_PASSWORD` blank so the backend can connect to MailHog.
+ If using Mailhog, make sure to leave the `SMTP_PASSWORD` blank so the backend can connect to MailHog.
## Docker for development
diff --git a/docs/images/email-mailhog-credentials.png b/docs/images/email-mailhog-credentials.png
new file mode 100644
index 000000000..8d5a11295
Binary files /dev/null and b/docs/images/email-mailhog-credentials.png differ
diff --git a/docs/images/email-sendgrid-create-key.png b/docs/images/email-sendgrid-create-key.png
new file mode 100644
index 000000000..1caa977a8
Binary files /dev/null and b/docs/images/email-sendgrid-create-key.png differ
diff --git a/docs/images/email-sendgrid-restrictions.png b/docs/images/email-sendgrid-restrictions.png
new file mode 100644
index 000000000..a70891a60
Binary files /dev/null and b/docs/images/email-sendgrid-restrictions.png differ
diff --git a/docs/mint.json b/docs/mint.json
index 92a952767..c94b9131a 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -102,18 +102,21 @@
{
"group": "Self-hosting",
"pages": [
- "self-hosting/overview",
- {
- "group": "Deployments options",
- "pages": [
- "self-hosting/deployments/linux",
- "self-hosting/deployments/kubernetes"
- ]
- },
- {
- "group": "Configuration",
- "pages": ["self-hosting/configuration/envars"]
- }
+ "self-hosting/overview"
+ ]
+ },
+ {
+ "group": "Deployment options",
+ "pages": [
+ "self-hosting/deployments/linux",
+ "self-hosting/deployments/kubernetes"
+ ]
+ },
+ {
+ "group": "Configuration",
+ "pages": [
+ "self-hosting/configuration/envars",
+ "self-hosting/configuration/email"
]
},
{
@@ -177,8 +180,7 @@
"pages": [
"contributing/overview",
"contributing/code-of-conduct",
- "contributing/developing",
- "contributing/FAQ"
+ "contributing/developing"
]
}
],
diff --git a/docs/self-hosting/configuration/email.mdx b/docs/self-hosting/configuration/email.mdx
new file mode 100644
index 000000000..b1e911fb6
--- /dev/null
+++ b/docs/self-hosting/configuration/email.mdx
@@ -0,0 +1,75 @@
+---
+title: "Email"
+description: ""
+---
+
+Infisical requires you to configure your own SMTP server for certain functionality like:
+
+- Sending email confirmation links to sign up.
+- Sending invite links for projects.
+- Sending alerts.
+
+We strongly recommend using an email service to act as your email server and provide examples for common providers.
+
+## General configuration
+
+By default, you need to configure the following SMTP [environment variables](https://infisical.com/docs/self-hosting/configuration/envars):
+
+- `SMTP_HOST`: Hostname to connect to for establishing SMTP connections.
+- `SMTP_USERNAME`: Credential to connect to host (e.g. team@infisical.com)
+- `SMTP_PASSWORD`: Credential to connect to host.
+- `SMTP_PORT`: Port to connect to for establishing SMTP connections.
+- `SMTP_SECURE`: If `true`, the connection will use TLS when connecting to server with special configs for SendGrid and Mailgun. If `false` (the default) then TLS is used if server supports the STARTTLS extension.
+- `SMTP_FROM_ADDRESS`: Email address to be used for sending emails (e.g. team@infisical.com).
+- `SMTP_FROM_NAME`: Name label to be used in `From` field (e.g. Team).
+
+Below you will find details on how to configure common email providers (not in any particular order).
+
+## Twilio SendGrid
+
+1. Create an account and configure [SendGrid](https://sendgrid.com) to send emails.
+2. Create a SendGrid API Key under Settings > [API Keys](https://app.sendgrid.com/settings/api_keys)
+3. Set a name for your API Key, we recommend using "Infisical," and select the "Restricted Key" option. You will need to enable the "Mail Send" permission as shown below:
+
+
+
+
+
+4. With the API Key, you can now set your SMTP environment variables:
+
+```
+SMTP_HOST=smtp.sendgrid.net
+SMTP_USERNAME=apikey
+SMTP_PASSWORD=SG.rqFsfjxYPiqE1lqZTgD_lz7x8IVLx # your SendGrid API Key from step above
+SMTP_PORT=587
+SMTP_SECURE=true
+SMTP_FROM_ADDRESS=hey@example.com # your email address being used to send out emails
+SMTP_FROM_NAME=Infisical
+```
+
+
+ Remember that you will need to restart Infisical for this to work properly.
+
+
+## Mailgun
+
+1. Create an account and configure [Mailgun](https://www.mailgun.com) to send emails.
+2. Obtain your Mailgun credentials in Sending > Overview > SMTP
+
+
+
+3. With your Mailgun credentials, you can now set up your SMTP environment variables:
+
+```
+SMTP_HOST=smtp.mailgun.org # obtained from credentials page
+SMTP_USERNAME=postmaster@example.mailgun.org # obtained from credentials page
+SMTP_PASSWORD=password # obtained from credentials page
+SMTP_PORT=587
+SMTP_SECURE=true
+SMTP_FROM_ADDRESS=hey@example.com # your email address being used to send out emails
+SMTP_FROM_NAME=Infisical
+```
+
+
+ Remember that you will need to restart Infisical for this to work properly.
+
\ No newline at end of file
diff --git a/docs/self-hosting/configuration/envars.mdx b/docs/self-hosting/configuration/envars.mdx
index 8bbb507d8..0b9fd5e71 100644
--- a/docs/self-hosting/configuration/envars.mdx
+++ b/docs/self-hosting/configuration/envars.mdx
@@ -3,9 +3,7 @@ title: "Environment Variables"
description: ""
---
-## The .env file
-
-Configuring Infisical requires setting some environment variables. There is a file called `.env.example` at the root directory of our main repo that you can use to create a `.env` before you start the server.
+Configuring Infisical requires setting some environment variables. There is a file called [`.env.example`](https://github.com/Infisical/infisical/blob/main/.env.example) at the root directory of our main repo that you can use to create a `.env` file before you start the server.
| Variable | Description | Default Value |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------- |
@@ -22,13 +20,13 @@ Configuring Infisical requires setting some environment variables. There is a fi
| `MONGO_USERNAME` | MongoDB username if using container | `None` |
| `MONGO_PASSWORD` | MongoDB password if using container | `None` |
| `SITE_URL` | ❗️ Site URL - should be an absolute URL including the protocol (e.g. `https://app.infisical.com`) | `None` |
-| `SMTP_HOST` | Hostname to connect to for establishing SMTP connections | `smtp.gmail.com` |
-| `SMTP_SECURE` | Use TLS when connecting to host. If false, TLS will be used if STARTTLS is supported | `false` |
-| `SMTP_PORT` | Port to connect to for establishing SMTP connections | `587` |
-| `SMTP_FROM_ADDRESS` | ❗️ Email address to be used for sending emails (e.g. `team@infisical.com`) | `None` |
-| `SMTP_FROM_NAME` | Name label to be used in From field (e.g. `Team`) | `Infisical` |
+| `SMTP_HOST` | ❗️ Hostname to connect to for establishing SMTP connections | `None` |
| `SMTP_USERNAME` | ❗️ Credential to connect to host (e.g. `team@infisical.com`) | `None` |
| `SMTP_PASSWORD` | ❗️ Credential to connect to host | `None` |
+| `SMTP_PORT` | Port to connect to for establishing SMTP connections | `587` |
+| `SMTP_SECURE` | If true, use TLS when connecting to host. If false, TLS will be used if STARTTLS is supported | `false` |
+| `SMTP_FROM_ADDRESS` | ❗️ Email address to be used for sending emails (e.g. `team@infisical.com`) | `None` |
+| `SMTP_FROM_NAME` | Name label to be used in From field (e.g. `Team`) | `Infisical` |
| `TELEMETRY_ENABLED` | `true` or `false`. [More](../overview). | `true` |
| `CLIENT_ID_HEROKU` | OAuth2 client ID for Heroku integration | `None` |
| `CLIENT_ID_VERCEL` | OAuth2 client ID for Vercel integration | `None` |
diff --git a/frontend/pages/404.tsx b/frontend/pages/404.tsx
new file mode 100644
index 000000000..6192f8773
--- /dev/null
+++ b/frontend/pages/404.tsx
@@ -0,0 +1,28 @@
+import React from "react";
+import Head from "next/head";
+import Image from "next/image";
+import Link from "next/link";
+
+export default function Custom404() {
+ return (
+