Skip to content

Commit bbfc889

Browse files
committed
feat: add Dockerfile, GHCR release workflow, APP_HOSTNAME env var
- Add multi-stage Dockerfile with standalone Next.js output - Add .dockerignore to keep image lean - Add GitHub Actions workflow to build and push to GHCR on release - Expose APP_HOSTNAME env var (default: seal3d.app) for SEO metadata - Replace hardcoded domain in layout, sitemap, and robots with env var - Update README with features list, Docker deployment docs - Bump version to 0.3.0
1 parent aca2f3a commit bbfc889

10 files changed

Lines changed: 148 additions & 9 deletions

File tree

.dockerignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
node_modules
2+
.next
3+
.open-next
4+
.wrangler
5+
.devenv
6+
.devenv.flake.nix
7+
.direnv
8+
.git
9+
.github
10+
coverage
11+
*.tsbuildinfo
12+
next-env.d.ts
13+
.env*
14+
.DS_Store

.github/workflows/release.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Release Container
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
permissions:
8+
contents: read
9+
packages: write
10+
11+
env:
12+
REGISTRY: ghcr.io
13+
IMAGE_NAME: ${{ github.repository }}
14+
15+
jobs:
16+
build-and-push:
17+
name: Build & Push to GHCR
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Log in to GHCR
24+
uses: docker/login-action@v3
25+
with:
26+
registry: ${{ env.REGISTRY }}
27+
username: ${{ github.actor }}
28+
password: ${{ secrets.GITHUB_TOKEN }}
29+
30+
- name: Extract metadata (tags, labels)
31+
id: meta
32+
uses: docker/metadata-action@v5
33+
with:
34+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
35+
tags: |
36+
type=semver,pattern={{version}}
37+
type=semver,pattern={{major}}.{{minor}}
38+
type=raw,value=latest
39+
40+
- name: Build and push
41+
uses: docker/build-push-action@v6
42+
with:
43+
context: .
44+
push: true
45+
tags: ${{ steps.meta.outputs.tags }}
46+
labels: ${{ steps.meta.outputs.labels }}

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ authors:
77
- family-names: "Quentin"
88
given-names: "Lars"
99
title: "Seal3D"
10-
version: 0.2.0
10+
version: 0.3.0
1111
date-released: 2026-02-20
1212
url: "https://github.com/valerius21/seal3d"

Dockerfile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
FROM oven/bun:1 AS base
2+
3+
# ------- deps -------
4+
FROM base AS deps
5+
WORKDIR /app
6+
COPY package.json bun.lock ./
7+
RUN bun install --frozen-lockfile
8+
9+
# ------- build -------
10+
FROM base AS build
11+
WORKDIR /app
12+
ARG APP_HOSTNAME=seal3d.app
13+
ENV APP_HOSTNAME=${APP_HOSTNAME}
14+
COPY --from=deps /app/node_modules ./node_modules
15+
COPY . .
16+
RUN bun run build
17+
18+
# ------- runner -------
19+
FROM gcr.io/distroless/cc-debian12 AS runner
20+
WORKDIR /app
21+
ENV NODE_ENV=production
22+
ENV HOSTNAME=0.0.0.0
23+
ENV PORT=3000
24+
25+
COPY --from=build /app/public ./public
26+
COPY --from=build /app/.next/standalone ./
27+
COPY --from=build /app/.next/static ./.next/static
28+
# Bun binary for the runtime
29+
COPY --from=base /usr/local/bin/bun /usr/local/bin/bun
30+
31+
EXPOSE 3000
32+
ENTRYPOINT ["/usr/local/bin/bun", "server.js"]

README.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@
1010

1111
Client-side file encryption using AES-256-GCM and Web Crypto API. Files never leave your device.
1212

13+
## Features
14+
15+
- **Encrypt & decrypt files** entirely in the browser — nothing is uploaded to a server
16+
- **AES-256-GCM** encryption via the Web Crypto API with PBKDF2 key derivation (100 000 iterations)
17+
- **Streaming chunk processing** (5 MiB blocks) so large files don't exhaust memory
18+
- **Authenticated chunk integrity** — per-file ID and block index as AAD prevent chunk-swapping and reordering attacks (inspired by gocryptfs)
19+
- **Passphrase generator** — cryptographically random 6-word passphrases from the EFF wordlist (~77.5 bits of entropy)
20+
- **Password visibility toggle** — show/hide the password field for easier entry
21+
- **Drag-and-drop file selection** with click-to-browse fallback
22+
- **Dark mode** — automatic light/dark theme support
23+
- **Keyboard shortcut** — press Enter in the password field to start processing
24+
- **PWA-ready** — SVG favicon and icons matching app branding
25+
1326
## Tech Stack
1427

1528
- Next.js 16 (App Router, Turbopack)
@@ -27,12 +40,38 @@ bun dev
2740

2841
## Deployment
2942

30-
Cloudflare Pages with OpenNext adapter.
43+
### Cloudflare Pages
44+
45+
Uses the OpenNext adapter for Cloudflare Pages.
3146

3247
```bash
3348
bun run deploy
3449
```
3550

51+
### Docker
52+
53+
A pre-built image is published to GHCR on every release:
54+
55+
```bash
56+
docker pull ghcr.io/valerius21/seal3d:latest
57+
docker run -p 3000:3000 ghcr.io/valerius21/seal3d:latest
58+
```
59+
60+
Or build locally:
61+
62+
```bash
63+
docker build -t seal3d .
64+
docker run -p 3000:3000 seal3d
65+
```
66+
67+
To use a custom hostname for metadata/SEO (default: `seal3d.app`):
68+
69+
```bash
70+
docker build --build-arg APP_HOSTNAME=my-domain.com -t seal3d .
71+
```
72+
73+
Then open http://localhost:3000.
74+
3675
## Citation
3776

3877
If you use Seal3D in your research, please cite it:
@@ -43,7 +82,7 @@ If you use Seal3D in your research, please cite it:
4382
@software{seal3d,
4483
author = {Mattfeld, Valerius Albert Gongjus and Quentin, Lars},
4584
title = {Seal3D},
46-
version = {0.1.0},
85+
version = {0.3.0},
4786
year = {2026},
4887
url = {https://github.com/valerius21/seal3d},
4988
note = {Client-side file encryption using AES-256-GCM and Web Crypto API}

app/layout.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ const geistSans = Geist({
77
subsets: ["latin"],
88
});
99

10+
const baseUrl = `https://${process.env.APP_HOSTNAME}`;
11+
1012
export const metadata: Metadata = {
11-
metadataBase: new URL('https://seal3d.app'),
13+
metadataBase: new URL(baseUrl),
1214
title: {
1315
default: "Seal3D - Client-Side File Encryption",
1416
template: "%s | Seal3D"
@@ -39,7 +41,7 @@ export const metadata: Metadata = {
3941
openGraph: {
4042
type: "website",
4143
locale: "en_US",
42-
url: "https://seal3d.app",
44+
url: baseUrl,
4345
title: "Seal3D - Client-Side File Encryption",
4446
description: "Secure AES-256-GCM file encryption powered by Web Crypto API. A modern hat.sh alternative with zero server uploads.",
4547
siteName: "Seal3D",
@@ -71,7 +73,7 @@ export const metadata: Metadata = {
7173
},
7274
manifest: "/manifest.json",
7375
alternates: {
74-
canonical: "https://seal3d.app"
76+
canonical: baseUrl
7577
},
7678

7779
};

app/robots.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { MetadataRoute } from 'next';
22

3+
const baseUrl = `https://${process.env.APP_HOSTNAME}`;
4+
35
export default function robots(): MetadataRoute.Robots {
46
return {
57
rules: {
68
userAgent: '*',
79
allow: '/',
810
},
9-
sitemap: 'https://seal3d.app/sitemap.xml',
11+
sitemap: `${baseUrl}/sitemap.xml`,
1012
};
1113
}

app/sitemap.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { MetadataRoute } from 'next';
22

3+
const baseUrl = `https://${process.env.APP_HOSTNAME}`;
4+
35
export default function sitemap(): MetadataRoute.Sitemap {
46
return [
57
{
6-
url: 'https://seal3d.app',
8+
url: baseUrl,
79
lastModified: new Date(),
810
changeFrequency: 'monthly',
911
priority: 1,

next.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { readFileSync } from "fs";
44
const { version } = JSON.parse(readFileSync("./package.json", "utf-8"));
55

66
const nextConfig: NextConfig = {
7+
output: "standalone",
78
env: {
89
APP_VERSION: version,
10+
APP_HOSTNAME: process.env.APP_HOSTNAME || "seal3d.app",
911
},
1012
};
1113

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "seal3d",
3-
"version": "0.2.0",
3+
"version": "0.3.0",
44
"private": true,
55
"scripts": {
66
"dev": "next dev",

0 commit comments

Comments
 (0)