Skip to content

Commit 10aa7b6

Browse files
authored
feat: SEO landing page, blog, and prerendered marketing pages (#77)
* chore: Bump yivi packages to 1.0.0-beta.3 * chore: Bump yivi packages to 1.0.0-beta.4 * feat: migrate from @e4a/pg-wasm to @e4a/pg-js SDK Replace direct usage of pg-wasm, yivi-core/client/web, conflux, and manual Cryptify upload/download logic with the pg-js TypeScript SDK. - Encryption: pg.encrypt({ files, recipients, sign }).upload() - Decryption: pg.open({ uuid }).inspect() / .decrypt() - Email decrypt: pg.open({ data }).decrypt() - Delete FileProvider.ts, utils.ts (Chunker), yivi-tools.ts - Simplify EncryptState type (remove modPromise, pkPromise, signing keys) - Import AttributeCon from pg-js instead of pg-wasm - Update all debug pages to match new types Closes #56 * fix: use published @e4a/pg-js ^0.4.0 instead of local file link The local file:// link doesn't work inside Docker containers. Use the published npm package instead. * fix: use dynamic imports for @e4a/pg-js to support SSR pg-js bundles WASM and browser APIs that can't load during SSR. Use lazy getPostGuard() and dynamic import() for error classes so the module is only loaded in the browser. * Revert "fix: use dynamic imports for @e4a/pg-js to support SSR" This reverts commit 8ddd5e1. * chore: migrate from yarn to npm - Replace yarn.lock with package-lock.json - Update Dockerfile to use npm ci / npm run build - Remove vite-plugin-wasm and vite-plugin-top-level-await (no longer needed since pg-js bundles WASM as base64) * chore: remove remaining yarn references - CI workflow: yarn → npm ci / npx - dev.Dockerfile: yarn → npm ci / npm run dev - GitLab CI: yarn → npm ci / npm run build * fix: resolve signing flow crash and improve error layout - Add `await tick()` before encryption to ensure the Yivi QR element is in the DOM before pg-js tries to find it (fixes "Element not found") - Keep file list visible during error state instead of hiding the left pane - Restore `confirmToSender: true` so senders receive a copy of the message * fix: update pg-js to 0.5.0, add sender encryption and remove unused polyfills - Update @e4a/pg-js from 0.4.0 to 0.5.0 (includes includeSender support and sender email extraction from Yivi JWT) - Add includeSender: true to sign call so sender can decrypt their own files - Add optional phone and date of birth attributes to Yivi signing session - Remove unused Node.js polyfill packages (rollup-plugin-node-polyfills, @esbuild-plugins/node-globals-polyfill, @esbuild-plugins/node-modules-polyfill) and clean up vite.config.ts — no longer needed after pg-wasm to pg-js migration - Pass confirmToSender: true so sender receives notification email * chore: update Docker images from node 20 to node 24 Local npm 11 generates lockfiles incompatible with npm 10 in node:20 images, causing `npm ci` to fail with missing picomatch@4.0.4. Node 24 ships with npm 11, matching the local toolchain. * chore: bump pg-js to ^0.6.0 * feat: mark sender attributes as optional in Yivi signing session Allows users to skip disclosing their name, phone number, and birthday when sending files. Only email remains required. * fix: use npm install instead of npm ci in dev Dockerfile npm ci requires package-lock.json which is not always present when the source is volume-mounted for hot reload. * chore: update cryptify and postguard submodules * feat: update @e4a/pg-js to ^0.7.0, use fluent extraAttribute API Replace pg.recipient.withPolicy() with the new fluent RecipientBuilder.extraAttribute() chaining. * feat: add prerendered landing page, blog, and SEO infrastructure Split routes into (marketing) and (app) layout groups: - Marketing pages (/, /about, /privacy, /blog) are prerendered as static HTML with full SEO meta tags, Open Graph, and Twitter Card support - App pages (/fileshare, /decrypt, /download, /addons) remain fully client-side with ssr=false to keep file encryption/decryption local Key changes: - Move encrypt page from / to /fileshare - New SEO landing page at / with returning visitor redirect to /fileshare - Blog system using mdsvex (markdown in repo) - SEO component for per-page meta tags - sitemap.xml and robots.txt - Rename SPA fallback from index.html to 200.html (nginx config updated) - Split i18n initialization per layout group for SSR compatibility * fix: include package-lock.json in dev Dockerfile Ensures mdsvex and other new dependencies are installed correctly in the dev container. * fix: only redirect returning visitors on full page load, not navbar clicks The returning visitor redirect to /fileshare was firing on every navigation to /, including clicking "Home" in the navbar. Now it only redirects on the initial page load (before SvelteKit hydrates). * fix: remove Home from navbar, logo already links to / * fix: align CTA button text by adding matching border to primary button * fix: let marketing footer flow naturally instead of sticking to viewport bottom * feat: add Docs link to navbar pointing to docs.postguard.eu * feat: move Docs link to far right of navbar, rename to Documentation/Documentatie * fix: move Documentation to last position in nav links list * feat: make hero section full viewport height with scroll indicator The hero (title, description, CTAs) now fills the viewport below the navbar. A bouncing chevron at the bottom hints at more content below. Features section appears on scroll. * fix: center hero content vertically in viewport * fix: make scroll arrow bounce straight down * feat: add PostGuard for Business section on landing page * fix: remove bottom padding on scroll indicator * fix: reduce gap between hero and features section * fix: restore scroll indicator bottom padding * fix: obfuscate contact email to prevent spam harvesting The mailto link is now assembled client-side via JS data attributes. Bots scanning raw HTML won't find an email address. * fix: use English 'PostGuard for Business' in both locales * Add coming soon on landing page * feat: add Blog to navbar and update PostGuard for Business section - Blog link added between About and Privacy Policy - Business section rewritten with current features (API, custom domains, self-hosted) and upcoming features (revocation, read receipts, whitelabeling, audit logging, BYOS, DNS verification) * feat: add For Business link to navbar pointing to business.postguard.eu * fix: rename navbar link to 'PostGuard for Business' * feat: replace Decrypt/Emailing nav items with Inbox button - Remove "Decrypt Emails" and "Emailing" from navbar - Add prominent "Inbox" button on the far right linking to /decrypt - Add extension install banner on the decrypt/inbox page * feat: update About team section to mention Yivi at Caesar Groep * fix: reduce inbox button padding to match toggle height * fix: point Thunderbird addon download to GitHub releases page The release assets are named with version numbers (e.g. postguard-tb-addon-0.9.0.xpi) so the old /downloads/ redirect to a generic filename 404'd. Now links to the releases page directly. * feat: add 'Looking ahead with PostGuard' roadmap blog post * fix: set roadmap blog post date to today * feat: add cover image to roadmap blog post * fix: constrain blog images to content width * fix: reduce space between date/author and blog content * fix: tighten spacing between date and author in blog header * feat: show cover images in blog index with matching card styling * feat: add welcome explanation to inbox when no email is selected Shows a friendly explanation of how the inbox works: upload the encrypted attachment, verify with Yivi, and read your email. Emphasises that everything stays in the browser. * fix: replace 'Oops' empty inbox text with neutral message * fix: remove fixed height and overflow on privacy page to prevent double scrollbar * fix: keep footer at bottom on short marketing pages * feat: replace copyright footer with useful link columns Three columns: Product (file sharing, inbox, extensions), Resources (about, blog, docs, privacy), Connect (obfuscated contact email, GitHub, business). Attribution line: Built by Yivi @ Caesar Groep. * fix: add more space between content and footer * fix: match footer top divider width with bottom divider * fix: inherit font and color on body, comment out Business nav link - Add font-family and color to html/body so the decrypt page matches the rest of the site - Comment out PostGuard for Business nav item for later activation * feat: re-enable PostGuard for Business nav link
1 parent 4d6dc94 commit 10aa7b6

40 files changed

Lines changed: 1253 additions & 80 deletions

docker/default.conf.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,6 @@ server {
8686

8787
location / {
8888
add_header Cache-Control "no-cache";
89-
try_files $uri $uri/ /index.html;
89+
try_files $uri $uri/ /200.html;
9090
}
9191
}

docker/dev.Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ FROM node:24-alpine
33
WORKDIR /app
44

55
# Copy package files
6-
COPY package.json ./
6+
COPY package.json package-lock.json ./
77

88
# Install dependencies
9-
RUN npm i
9+
RUN npm install
1010

1111
# The source code will be mounted as a volume
1212
# This allows hot reloading during development

package-lock.json

Lines changed: 124 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"eslint-config-prettier": "^8.8.0",
2525
"eslint-plugin-svelte": "^2.35.1",
2626
"lazysizes": "^5.3.2",
27+
"mdsvex": "^0.12.7",
2728
"prettier": "^3.1.0",
2829
"prettier-plugin-svelte": "^3.2.6",
2930
"rollup": "^4.57.1",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: "Introducing PostGuard: Easy End-to-End Encryption for Everyone"
3+
description: "PostGuard makes end-to-end encryption accessible. Learn how identity-based encryption simplifies secure email and file sharing."
4+
date: "2026-04-15"
5+
author: "PostGuard Team"
6+
---
7+
8+
# Introducing PostGuard
9+
10+
PostGuard offers free and easy-to-use end-to-end encryption for emails and files. Only the intended recipients can decrypt and read what you send.
11+
12+
## Why PostGuard?
13+
14+
Most email applications and file sharing tools do not apply end-to-end encryption by default. This means unauthorized parties could access and tamper with your emails and files — not appropriate for sensitive information.
15+
16+
PostGuard solves this with **Identity-Based Encryption (IBE)**, making encryption as simple as knowing someone's email address. No need to exchange public keys or manage certificates.
17+
18+
## How it works
19+
20+
1. **Select recipients** — just use their email address
21+
2. **Choose your files** — drag and drop or browse
22+
3. **Send** — PostGuard encrypts everything locally in your browser before it leaves your device
23+
24+
Your files are never sent unencrypted. The encryption happens entirely in your browser, so we never see your data.
25+
26+
## Get started
27+
28+
Head over to [postguard.eu/fileshare](/fileshare) to try it out, or install the [Thunderbird add-on](/addons) for seamless email encryption.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: "Looking ahead with PostGuard"
3+
description: "The Yivi team shares PostGuard's roadmap for 2026 — free personal use, business features, and exciting proof of concepts with NotifyNL and Bereken Je Recht."
4+
date: "2026-04-16"
5+
author: "Ruben Hensen"
6+
image: "/blog/postguard_cover.png"
7+
---
8+
9+
# Looking ahead with PostGuard
10+
11+
![PostGuard cover](/blog/postguard_cover.png)
12+
13+
The Yivi team is working hard on Yivi itself to become EUDI compliant, but besides Yivi we're also working hard on other projects like PostGuard. In this blog we'll tell you about PostGuard and what PostGuard's plans are for the coming year. We hope to get you just as excited about PostGuard as we are. And that when you want to send files, you'll do it with PostGuard of course!
14+
15+
Let me first introduce myself. I'm Ruben and last February I started at Yivi as an open source developer. I don't have a fixed role at Yivi, but I've been busy working on PostGuard. PostGuard is an identity-based, end-to-end encrypted file sharing service that originated from the [iHub](https://ihub.ru.nl/) at Radboud University. It was originally started as an NWO project under the name Encryption4All and co-funded by [NLNet](https://nlnet.nl/). The encryption is handled using Yivi, which we call identity-based encryption. The "Post" in PostGuard refers to the focus on e-mail, which for many people remains the default way to send a file to someone. Think of it as an alternative to Zivver or WeTransfer, but European, open source and with a full embrace of [Privacy by Design](https://www.sfu.ca/~palys/Cavoukian-2011-PrivacyByDesign-7FoundationalPrinciples.pdf). We could explain it at length, but the easiest thing is to just try it out at [PostGuard.eu](https://postguard.eu). Once you're done, we'll take you through PostGuard's roadmap.
16+
17+
## PostGuard for you
18+
19+
Our goal is for you to use PostGuard, simply because it's convenient, secure and works well. No crazy sales tricks or hidden subscriptions, no tracking by or for advertisers. Our goal is for everyone to see that the identity wallet is _here to stay_ and that managing your own data can actually be pleasant and easy.
20+
21+
Unless you happen to live in Nijmegen, the chances are very small that you've found a place where you can use Yivi, and we think that's a shame. That's why we're going to make PostGuard and the Thunderbird and Outlook extensions available for free for personal use. This way you can safely and freely send large files to your friends or family. You can do this in two ways:
22+
23+
1. Via the website, just like WeTransfer. You upload a file via the website and fill in your own email and the recipient's. Your files are then emailed to the recipients by PostGuard.
24+
2. Via the extensions. We hope that with the Thunderbird and Outlook extensions, emailing large files feels like sending a regular attachment in an email. An added benefit is that the email is also sent from your email in your name.
25+
26+
## PostGuard for Business
27+
28+
The second part of our roadmap is PostGuard for Business. The goal here is quite simple to explain: our goal is to become a full end-to-end encryption email service with everything businesses expect. That includes download verification, email revocation, auditing trails, and most importantly, programmatic emailing from an internal application. To achieve this, we're participating in several Proof of Concepts (PoC) where we test the business case. We're proud to say that we're already working on two projects: information-rich notifications with NotifyNL and Bereken Je Recht ("Calculate Your Rights"). We'll tell you a bit more about both.
29+
30+
### Information-rich notifications with NotifyNL
31+
32+
"A message has arrived for you." When you receive an email from the government right now, it deliberately contains very little — no links or files — to make sure people navigate to a trusted environment on their own to read the message. Super secure of course, but not very user-friendly. With NotifyNL we're setting up a PoC where citizens receive an encrypted message in their email via NotifyNL. This way the government can use Yivi's attribute verification to be certain that the message can only be read by the right person. And the citizen gets the convenience of reading a regular email where all the information they need is included right away.
33+
34+
### Bereken Je Recht
35+
36+
[berekenjerecht.nl](https://www.berekenjerecht.nl/) wants to make it easy for citizens to claim the benefits they're entitled to. Bereken Je Recht helps you figure out which benefits you're entitled to, and then also apply for those benefits with the right agencies. The ultimate goal is to shorten the process, which currently often takes around 10 weeks, to 10 minutes. There's a lot to gain here by issuing and verifying attributes with various municipal and government agencies. PostGuard comes into play when sending important documents to agencies that don't yet work with identity wallet issuers and verifiers. Bereken Je Recht knows which documents you need to send for a particular application. And PostGuard can securely encrypt and email these to the relevant organizations!
37+
38+
## In closing
39+
40+
We're very excited to continue working on PostGuard, and we hope we've been able to share our enthusiasm about PostGuard with you. We believe PostGuard can truly make a difference in the world, helping you with a digital existence that works _for_ you instead of against you. That sounds a bit like a sales pitch you'd find on LinkedIn, but we truly believe it and we're happy to stand up for such an existence. Besides, PostGuard is free, so there's not much to sell.
41+
42+
**[Try PostGuard now!](/fileshare)**

src/lib/components/Header.svelte

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
import ThemeSwitcher from './ThemeSwitcher.svelte'
1010
1111
let items = [
12-
{ name: 'fs', route: '/' },
13-
{ name: 'addons', route: '/addons' },
14-
{ name: 'decrypt', route: '/decrypt' },
12+
{ name: 'fs', route: '/fileshare' },
1513
{ name: 'about', route: '/about' },
14+
{ name: 'blog', route: '/blog' },
1615
{ name: 'pol', route: '/privacy' },
16+
{ name: 'business', route: 'https://business.postguard.eu' },
17+
{ name: 'docs', route: 'https://docs.postguard.eu' },
1718
]
1819
1920
function isSelected(route: String) {
@@ -38,9 +39,12 @@
3839
</ul>
3940
<LocaleSwitcher />
4041
<ThemeSwitcher />
42+
<a href="/decrypt" class="inbox-btn" class:selected={isSelected('/decrypt')}>
43+
{$_('header.inbox')}
44+
</a>
4145
</div>
4246
<Hamburger
43-
{items}
47+
items={[...items, { name: 'inbox', route: '/decrypt' }]}
4448
/>
4549
</div>
4650

@@ -79,12 +83,36 @@
7983
gap: 1rem;
8084
}
8185
86+
8287
@media only screen and (min-width: 768px) {
8388
.pg-desktop-menu {
8489
display: flex;
8590
}
8691
}
8792
93+
.inbox-btn {
94+
padding: 0.25rem 1rem;
95+
background: var(--pg-primary);
96+
color: white;
97+
border-radius: var(--pg-border-radius-sm);
98+
text-decoration: none;
99+
font-weight: var(--pg-font-weight-semibold);
100+
font-size: var(--pg-font-size-sm);
101+
transition: opacity 0.2s ease;
102+
white-space: nowrap;
103+
104+
&:hover {
105+
opacity: 0.9;
106+
}
107+
108+
&.selected {
109+
opacity: 0.85;
110+
box-shadow: 0 0 0 2px var(--pg-primary);
111+
background: transparent;
112+
color: var(--pg-primary);
113+
}
114+
}
115+
88116
.pg-desktop-menu ul li {
89117
display: inline-block;
90118
position: relative;

src/lib/components/SEO.svelte

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script>
2+
let { title = '', description = '', ogImage = '', ogType = 'website', canonical = '' } = $props()
3+
const siteName = 'PostGuard'
4+
const defaultDescription = 'PostGuard offers free and easy-to-use end-to-end encryption for emails and files.'
5+
const defaultImage = '/pg_logo.png'
6+
</script>
7+
8+
<svelte:head>
9+
<title>{title ? `${title} | ${siteName}` : siteName}</title>
10+
<meta name="description" content={description || defaultDescription} />
11+
<meta property="og:title" content={title || siteName} />
12+
<meta property="og:description" content={description || defaultDescription} />
13+
<meta property="og:image" content={ogImage || defaultImage} />
14+
<meta property="og:type" content={ogType} />
15+
<meta property="og:site_name" content={siteName} />
16+
{#if canonical}<link rel="canonical" href={canonical} />{/if}
17+
<meta name="twitter:card" content="summary_large_image" />
18+
</svelte:head>

src/lib/global.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ body {
2222
margin: 0;
2323
overflow-x: hidden;
2424
background: var(--pg-general-background);
25+
font-family: var(--pg-font-family);
26+
color: var(--pg-text);
2527
z-index: -5;
2628
-webkit-font-smoothing: antialiased;
2729
-moz-osx-font-smoothing: grayscale;

0 commit comments

Comments
 (0)