Skip to content

Handle custom backends #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/ci-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI Check and Build

on:
pull_request:
branches: [main]

jobs:
ci-check-and-build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "20"

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 9

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build project
run: pnpm run build
env:
NEXT_PUBLIC_WEBSOCKET_HOST: "dummy"
NEXT_PUBLIC_WEBSOCKET_APP_KEY: "dummy"

- name: Run ci-check
run: pnpm run ci-check
env:
NEXT_PUBLIC_WEBSOCKET_HOST: "dummy"
NEXT_PUBLIC_WEBSOCKET_APP_KEY: "dummy"
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# subtle.sh

Discovered openpgp.js and soketi, and thought it would be fun to build an E2E encrypted app. Next time you're sending a secret over Slack, email, or any other plain text messaging, remember you have easy alternatives. Whether you choose subtle.sh or a pigeon is up to you, but here are some points to help you decide:

- a pigeon is great but not zero-config.
- subtle.sh is less likely to get intercepted.
- Both support custom servers, but a pigeon offers a wider range of supported providers.
- subtle.sh uses a more modern tech stack (Next.js, Soketi, React, Tailwind).
- a pigeon is more established and has long-term support.

All jokes aside, this could be genuinely useful if you don’t share a password vault with someone and need to quickly send credentials. No installation or setup required—just generate a fresh session and share it. Once the tab is closed, all keys are discarded, and the secret disappears with them.

## Host your own server

You can host your own server if you want to keep complete ownership of your sessions.

- [Setup a backend on a cheap VPS](/docs/self-host-soketi/)
- Host on railway.app from a template (TO-DO)

## Development

To run the app locally, you need to have `pnpm` installed. Then, you can install the dependencies and start the development server with:
Expand Down
15 changes: 15 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ export const metadata: Metadata = {
template: `%s - ${siteConfig.name}`,
},
description: siteConfig.description,
keywords: [
"subtle",
"subtle.sh",
"encrypted messages",
"privacy",
"secure",
"pgp",
"soketi",
"nextjs",
"react",
"tailwind",
"shadcn",
"ui",
],
icons: {
icon: "/favicon.ico",
shortcut: "/favicon-16x16.png",
Expand All @@ -34,6 +48,7 @@ interface RootLayoutProps {

export default function RootLayout({ children }: RootLayoutProps) {
return (
// https://github.com/shadcn/next-contentlayer/issues/7
<html lang="en" suppressHydrationWarning>
<head />
<body
Expand Down
107 changes: 63 additions & 44 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,77 @@
"use client";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { StartSessionLink } from "@/components/start-session-link";
import { Icons } from "@/components/icons";
import { SettingsModal } from "@/components/settings-modal";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import Link from "next/link";
import { buttonVariants } from "@/components/ui/button";
import { siteConfig } from "@/config/site";

const queryClient = new QueryClient();

export default function Home() {
return (
<section className="container grid items-center gap-6 pb-8 pt-6 md:py-10">
<div className="mx-auto flex size-64 items-center justify-center">
<Icons.logo4 />
</div>
<h1 className="text-center text-2xl leading-tight tracking-tighter">
Send encrypted messages without leaving a trace.
</h1>
<div className="flex gap-4">
<div className="m-auto">
<QueryClientProvider client={queryClient}>
<section className="container grid items-center gap-6 pb-8 pt-6 md:py-10">
<div className="mx-auto flex size-64 items-center justify-center">
<Icons.logo4 />
</div>
<h1 className="text-center text-2xl leading-tight tracking-tighter">
Send encrypted messages without leaving a trace.
</h1>

<div className="m-auto flex gap-2">
<Link
className={buttonVariants({
variant: "secondary",
})}
href={siteConfig.links.github}
target="_blank"
>
<Icons.gitHub className="size-4" />
</Link>
<StartSessionLink />
<SettingsModal />
</div>
</div>
<ul className="m-auto my-8 flex max-w-[789px] flex-col gap-3 font-semibold opacity-50">
<li className="text-left">
<HoverCard>
<HoverCardTrigger className="text-lg underline underline-offset-8 hover:cursor-pointer">
One click setup for quick sharing
</HoverCardTrigger>
<HoverCardContent className="text-center">
No account or settings to fiddle with. Just share a link.
</HoverCardContent>
</HoverCard>
</li>
<li className="text-center">
<HoverCard>
<HoverCardTrigger className="text-lg underline underline-offset-8 hover:cursor-pointer">
MiLiTaRy GrAdE pOsT qUaNtUm CrYpToGrApHy (good ol&apos; PGP)
</HoverCardTrigger>
<HoverCardContent className="text-center">
No really, that&apos;s all there is. It&apos;s just a PGP client
with in-memory keys.
</HoverCardContent>
</HoverCard>
</li>
<li className="text-right">
<HoverCard>
<HoverCardTrigger className="text-lg underline underline-offset-8 hover:cursor-pointer">
Bring your own backend
</HoverCardTrigger>
<HoverCardContent className="text-center">
Simple by design, no history or persistence.
</HoverCardContent>
</HoverCard>
</li>
</ul>
</section>
<ul className="m-auto my-8 flex max-w-[789px] flex-col gap-3 font-semibold opacity-50">
<li className="text-left">
<HoverCard>
<HoverCardTrigger className="text-lg underline underline-offset-8 hover:cursor-pointer">
One click setup for quick sharing
</HoverCardTrigger>
<HoverCardContent className="text-center">
No account or settings to fiddle with. Just share a link.
</HoverCardContent>
</HoverCard>
</li>
<li className="text-center">
<HoverCard>
<HoverCardTrigger className="text-lg underline underline-offset-8 hover:cursor-pointer">
MiLiTaRy GrAdE pOsT qUaNtUm CrYpToGrApHy (good ol&apos; PGP)
</HoverCardTrigger>
<HoverCardContent className="text-center">
No really, that&apos;s all there is. It&apos;s just a PGP client
with in-memory keys.
</HoverCardContent>
</HoverCard>
</li>
<li className="text-right">
<HoverCard>
<HoverCardTrigger className="text-lg underline underline-offset-8 hover:cursor-pointer">
Bring your own backend
</HoverCardTrigger>
<HoverCardContent className="text-center">
Simple by design, no history or persistence.
</HoverCardContent>
</HoverCard>
</li>
</ul>
</section>
</QueryClientProvider>
);
}
6 changes: 4 additions & 2 deletions components/copy-encrypted-text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ export const CopyEncryptedText = ({ encryptedText }: Props) => {
const decrypted = await decryptText(encryptedText);

if (decrypted.isErr()) {
// TO-DO: show error decrypting failed
console.error(decrypted.error);
toast({
title: "Error decrypting content",
description: decrypted.error,
});
setLoading(false);
return;
}
Expand Down
33 changes: 1 addition & 32 deletions components/icons.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
import { Moon, SunMedium, Twitter, type LucideProps } from "lucide-react";
import { Moon, SunMedium, type LucideProps } from "lucide-react";

export const Icons = {
sun: SunMedium,
moon: Moon,
twitter: Twitter,
logo: (props: LucideProps) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
d="M11.572 0c-.176 0-.31.001-.358.007a19.76 19.76 0 0 1-.364.033C7.443.346 4.25 2.185 2.228 5.012a11.875 11.875 0 0 0-2.119 5.243c-.096.659-.108.854-.108 1.747s.012 1.089.108 1.748c.652 4.506 3.86 8.292 8.209 9.695.779.25 1.6.422 2.534.525.363.04 1.935.04 2.299 0 1.611-.178 2.977-.577 4.323-1.264.207-.106.247-.134.219-.158-.02-.013-.9-1.193-1.955-2.62l-1.919-2.592-2.404-3.558a338.739 338.739 0 0 0-2.422-3.556c-.009-.002-.018 1.579-.023 3.51-.007 3.38-.01 3.515-.052 3.595a.426.426 0 0 1-.206.214c-.075.037-.14.044-.495.044H7.81l-.108-.068a.438.438 0 0 1-.157-.171l-.05-.106.006-4.703.007-4.705.072-.092a.645.645 0 0 1 .174-.143c.096-.047.134-.051.54-.051.478 0 .558.018.682.154.035.038 1.337 1.999 2.895 4.361a10760.433 10760.433 0 0 0 4.735 7.17l1.9 2.879.096-.063a12.317 12.317 0 0 0 2.466-2.163 11.944 11.944 0 0 0 2.824-6.134c.096-.66.108-.854.108-1.748 0-.893-.012-1.088-.108-1.747-.652-4.506-3.859-8.292-8.208-9.695a12.597 12.597 0 0 0-2.499-.523A33.119 33.119 0 0 0 11.573 0zm4.069 7.217c.347 0 .408.005.486.047a.473.473 0 0 1 .237.277c.018.06.023 1.365.018 4.304l-.006 4.218-.744-1.14-.746-1.14v-3.066c0-1.982.01-3.097.023-3.15a.478.478 0 0 1 .233-.296c.096-.05.13-.054.5-.054z"
/>
</svg>
),
logo2: () => (
<svg
version="1.1"
// width="412.99371"
// height="751.77576"
// width="206.496855"
// height="50.355152"
width="100%"
height="100%"
viewBox="0 0 412.99371 751.77576"
xmlns="http://www.w3.org/2000/svg"
>
<defs id="defs1" />
<g id="g1" transform="translate(-279.13247,-104.39919)">
<path
fill="#fff"
d="m 429.59377,854.28091 c -16.44268,-3.56562 -26.61289,-8.1653 -39.74159,-17.97395 -6.78296,-5.06765 -18.91002,-17.73449 -20.0421,-20.93417 -0.15603,-0.441 3.89397,-1.0726 9,-1.40355 25.72485,-1.66741 46.86292,-13.38393 55.92103,-30.9962 1.07492,-2.09004 3.92996,-13.34004 6.34454,-25 5.11739,-24.71178 12.1919,-55.36307 13.12757,-56.87702 0.7717,-1.24865 0.60125,-1.27147 34.01756,4.55354 14.36986,2.50489 27.30096,5.08924 28.73579,5.74299 2.95127,1.34469 4.7113,5.45618 10.34544,24.16722 l 3.54911,11.78665 -6.4732,6.71335 c -7.48959,7.76744 -9.67411,12.65057 -7.74396,17.31037 2.73282,6.59761 7.90406,6.50515 19.4095,-0.34703 17.15859,-10.21897 19.50949,-14.45471 15.27365,-27.51929 -7.6467,-23.58465 -18.73836,-53.69017 -20.389,-55.34081 -1.33252,-1.33252 -5.33443,-2.48098 -13.3681,-3.83635 -6.30643,-1.06396 -19.11624,-3.32863 -28.46624,-5.03261 -50.09931,-9.13025 -47.61685,-8.47461 -49.52443,-13.07992 -0.90417,-2.18286 -0.86353,-3.40249 0.18638,-5.59304 2.51495,-5.24727 3.69439,-5.19399 43.48872,1.96477 20.26713,3.64594 39.99933,7.11318 43.84933,7.70498 8.54177,1.31301 13.42989,3.70168 15.76991,7.70628 0.97345,1.66592 5.12087,12.75271 9.21649,24.63732 4.09562,11.88461 8.12001,22.91066 8.9431,24.50234 2.50675,4.84751 7.76609,5.59249 12.2504,1.73525 4.90329,-4.21764 5.15969,-3.24893 -15.24539,-57.59892 -6.52123,-17.36964 -8.81731,-22.13346 -11.46488,-23.7869 -1.09803,-0.68573 -16.72789,-3.71657 -34.73303,-6.7352 -57.69846,-9.67337 -59.45608,-10.03072 -61.45056,-12.4938 -2.47242,-3.05331 -2.24903,-7.20052 0.52841,-9.80979 2.73894,-2.57309 4.95726,-2.66178 17.06168,-0.6821 5.01813,0.82071 25.09887,3.98498 44.62387,7.0317 19.525,3.04673 36.923,5.9971 38.66222,6.55638 1.73922,0.55928 4.30286,2.15752 5.69699,3.55165 2.22527,2.22527 13.28269,28.45341 23.11409,54.82649 1.42669,3.82714 3.32358,7.76464 4.21533,8.75 2.4989,2.76126 7.74321,2.32932 10.23879,-0.84329 3.92377,-4.98826 4.12264,-4.34319 -15.52746,-50.36514 -11.88885,-27.84454 -15.83767,-35.6035 -19.39996,-38.1186 -2.38715,-1.6854 -17.25966,-4.20252 -85,-14.38593 -9.625,-1.44693 -17.83535,-2.95818 -18.24522,-3.35834 -0.40987,-0.40016 2.37058,-13.08222 6.17877,-28.18235 13.10766,-51.97408 19.24895,-87.678 20.59432,-119.73023 0.72663,-17.31115 1.14461,-19.9789 3.31198,-21.13884 2.86464,-1.53311 7.57346,1.4494 10.01884,6.3458 6.52063,13.05629 9.33742,36.64177 10.81649,90.56849 0.50018,18.23658 0.96356,24.94086 1.486,21.5 3.46026,-22.78963 4.51857,-98.48076 1.59394,-114 -2.62527,-13.93075 -8.38462,-22.72177 -17.90547,-27.33074 -11.21997,-5.43149 -24.02358,0.0388 -27.69052,11.83074 -0.59861,1.925 -1.56462,11.825 -2.14667,22 -2.95369,51.63394 -9.81333,86.68647 -29.74581,152 -3.60877,11.825 -7.17213,23.75 -7.91859,26.5 -1.5153,5.58255 -7.87368,25.75789 -12.92138,41 -1.82143,5.5 -5.10529,15.85 -7.29748,23 -2.19219,7.15 -5.17817,16.825 -6.63552,21.5 -1.45734,4.675 -4.15583,13.675 -5.99665,20 -1.84081,6.325 -4.06587,13.19737 -4.94456,15.27193 l -1.59763,3.77192 -6.03213,-4.09634 c -14.74952,-10.0162 -34.66992,-28.98715 -48.24709,-45.94751 -6.86824,-8.57969 -21.97669,-31.14714 -25.68033,-38.35865 -2.15494,-4.19597 -2.29069,-4.98799 -0.99528,-5.8069 0.825,-0.52154 12.525,-7.80318 26,-16.18142 l 24.49999,-15.23317 0.5,-39.71416 0.5,-39.71416 2.99738,3.17652 c 4.79955,5.08639 12.43858,10.77082 18.76838,13.9661 5.23874,2.6445 18.98862,6.81011 22.61217,6.85049 0.75786,0.008 -2.55113,-2.77944 -7.3533,-6.19531 -10.31403,-7.33653 -22.92868,-19.52077 -30.32399,-29.28934 -7.13735,-9.42783 -15.60157,-26.90125 -18.6496,-38.5 -2.8503,-10.84631 -6.04387,-29.64166 -7.09387,-41.75 -0.68099,-7.8529 -1.56962,-10.0053 -2.80338,-6.79018 -0.3081,0.80291 -2.44877,2.82791 -4.75702,4.5 -4.72665,3.42398 -10.57978,4.03512 -16.1911,1.69056 -7.87352,-3.28977 -18.30379,-19.16035 -24.08692,-36.65038 -7.0075,-21.1929 -10.01639,-39.40875 -10.07597,-61 -0.0389,-14.11026 0.16335,-15.94719 2.25608,-20.48754 4.10193,-8.8995 9.14715,-12.51246 17.47269,-12.51246 8.97761,0 19.01029,7.89156 25.1085,19.75 2.19911,4.27634 2.59863,4.10125 1.5268,-0.6691 -5.98281,-26.62754 -7.39713,-70.02858 -3.06584,-94.0809 3.58047,-19.88284 11.9916,-40.06639 24.20788,-58.0897 8.04909,-11.87524 27.21506,-31.15184 39.95111,-40.18173 25.07528,-17.77842 59.36158,-29.31257 90.57604,-30.47039 65.32932,-2.42324 123.33616,28.6223 153.99278,82.41768 18.17744,31.89726 23.53873,66.87354 17.60065,114.82414 -1.22599,9.9 -2.65492,19.8 -3.17541,22 -1.47163,6.22025 -1.25276,6.21034 2.8444,-0.12876 6.67105,-10.32142 14.37903,-15.37124 23.46246,-15.37124 15.28063,0 22.27058,15.19712 19.71546,42.86416 -2.57971,27.9333 -7.95632,48.30986 -17.77791,67.37561 -5.77605,11.21252 -11.42348,17.99929 -16.95635,20.37724 -7.06885,3.03807 -15.84603,0.72379 -19.81692,-5.22514 l -2.07467,-3.10813 -1.80263,14.46806 c -4.20844,33.77734 -9.71963,50.32093 -23.79155,71.41786 -7.18871,10.77748 -22.74167,27.30674 -30.16727,32.06094 -4.15611,2.66093 -4.7143,3.32567 -2.99923,3.57173 3.32403,0.47689 14.90605,-2.53346 20.33854,-5.28631 2.7228,-1.37975 7.83185,-5.20395 11.35345,-8.49824 l 6.40291,-5.98961 0.62893,5.23591 c 0.34592,2.87976 0.63285,13.4492 0.63763,23.48766 l 0.009,18.25174 20.73659,13.74826 c 17.99023,11.92744 21.13568,14.40068 23.75,18.67442 2.66359,4.3543 3.00998,5.8054 2.98387,12.5 -0.0203,5.2002 -0.56693,8.57335 -1.74426,10.76308 l -1.71472,3.18924 4.02414,2.2617 c 2.22064,1.24807 3.81294,2.81208 3.55289,3.48977 -0.93061,2.42512 -22.97399,34.97893 -27.33344,40.36619 -17.51704,21.64699 -34.17943,37.59477 -51.74561,49.52639 l -8.29894,5.63695 -0.70399,5.63459 c -1.89245,15.14663 -15.0313,43.45542 -27.13538,58.46552 -14.3697,17.81966 -32.24883,33.05668 -48.37115,41.22305 -6.58294,3.33443 -22.70138,8.28066 -32.62216,10.0107 -11.78712,2.0555 -30.68239,1.83378 -41.37784,-0.48554 z M 640.36346,634.53124 c 7.38912,-3.40045 9.63754,-12.20702 4.37233,-17.12547 -1.57189,-1.46836 -9.55939,-6.91552 -17.75,-12.10479 -8.19061,-5.18928 -23.66702,-15.28049 -34.39202,-22.42493 -10.725,-7.14443 -22.1584,-14.36532 -25.40755,-16.04641 l -5.90756,-3.05653 -28.13517,-0.29292 -28.13517,-0.29291 -1.19782,10.71903 c -0.6588,5.89547 -0.94037,10.97729 -0.62571,11.29292 0.31466,0.31563 10.5479,0.81808 22.74054,1.11655 12.51146,0.30627 23.25749,1.0174 24.66844,1.63245 1.375,0.59939 16.225,10.2187 33,21.37624 34.2872,22.80543 38.62386,25.49569 43.242,26.82537 3.8248,1.10126 8.95003,0.48802 13.52769,-1.6186 z"
id="path1"
/>
</g>
</svg>
),
logo4: () => (
<svg
width="155.61469mm"
Expand Down
1 change: 0 additions & 1 deletion components/messages/messages-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { columns } from "./columns";
import { DataTable } from "./data-table";
import { useMemo } from "react";

// TO-DO: implement better handling of invalid signatures
export const MessagesTable = () => {
const messages = useMessageStore((state) => state.messages);
const data = useMemo(
Expand Down
8 changes: 6 additions & 2 deletions components/new-message/use-new-message-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ export const useNewMessageForm = ({
},
onSubmit: async (values, form) => {
if (myKeys.public === undefined) {
// TO-DO: show error missing public key
toast({
title: "Error missing public key",
});
return;
}

Expand All @@ -48,7 +50,9 @@ export const useNewMessageForm = ({
]);

if (label.isErr() || message.isErr()) {
// TO-DO: show error encryption failed
toast({
title: "Error encrypting message",
});
return;
}

Expand Down
Loading
Loading