Skip to content
vauchiPublic

About

Vauchi — Landing page (vauchi.app) (mirror of gitlab.com/vauchi)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Mirror: This repo is a read-only mirror of gitlab.com/vauchi/website. Please open issues and merge requests there.

Pipeline REUSE

Vauchi Website

Landing page for Vauchi at vauchi.app.

Requirements

System Dependencies

Tool Required Purpose
Python 3 For build scripts Content validation and manifest generation
Docker For deployment Containerization
Nginx In container Static file serving

Python Dependencies

pip install jsonschema

Required for validate-content.py schema validation.

Overview

Static website explaining what Vauchi is and linking to app downloads. Also serves dynamic app content (locales, themes, networks) via JSON manifests with checksums.

Structure

website/
├── public/                    # Static files served by nginx
│   ├── index.html             # Main landing page
│   ├── logo.png               # Brand logo
│   └── app-files/             # Generated app content (via build script)
│       ├── manifest.json      # Content manifest with checksums
│       ├── networks.json      # Network configurations
│       ├── locales/           # Localization files
│       └── themes/            # Theme definitions
│
├── app-files-src/             # Source content (before build)
│   ├── networks.json          # Network definitions
│   ├── locales/               # Language files (en.json, etc.)
│   └── schemas/               # JSON validation schemas
│       ├── networks.schema.json
│       ├── locales.schema.json
│       └── manifest.schema.json
│
├── scripts/                   # Build and validation scripts
│   ├── build-manifest.py      # Build manifest and copy content
│   └── validate-content.py    # Validate JSON against schemas
│
├── nginx.conf                 # Nginx server configuration
├── Dockerfile                 # Container definition
└── .gitlab-ci.yml             # CI/CD pipeline

Development

Local Preview

# Using Python
python -m http.server 8000 -d public

# Using Node
npx serve public

Then open http://localhost:8000

Building App Content

Validate and build the app-files manifest:

# Validate content against schemas
python scripts/validate-content.py

# Build manifest and copy to public/app-files/
python scripts/build-manifest.py

# With options
python scripts/build-manifest.py --version 1.0.0 --output public/app-files

validate-content.py

Validates all JSON content files against their schemas:

python scripts/validate-content.py [--src app-files-src]

# Validates:
# - networks.json vs networks.schema.json
# - locales/*.json vs locales.schema.json

build-manifest.py

Generates content manifest with SHA-256 checksums:

python scripts/build-manifest.py [options]

Options:
  --version VERSION    Content version (default: 1.0.0)
  --output DIR         Output directory (default: public/app-files)
  --base-url URL       CDN URL (default: https://vauchi.app/app-files/)
  --src DIR            Source directory (default: app-files-src)

The generated manifest.json contains checksums for integrity verification by the mobile apps.

Docker

# Build
docker build -t vauchi-website .

# Run
docker run -p 8080:80 vauchi-website

The image runs as the unprivileged nginx user. Technical error and waitlist service logs are written to /var/log/vauchi.

Deployment

Deployed via Kamal to the same server as vauchi-relay. See vauchi/infra for deployment config.

# From infra repo
kamal deploy -c config/deploy.vauchi-landing.yml

CI/CD Pipeline

The .gitlab-ci.yml pipeline:

  1. Validates content (validate-content.py)
  2. Builds manifest (build-manifest.py)
  3. Builds Docker image
  4. Deploys via Kamal (on main branch)

Adding Content

New Locale

  1. Add {lang}.json in the locales repo — the canonical source. This repo carries no locale copies; the build resolves the sibling checkout or the GitLab API and fails loudly otherwise.
  2. Run python scripts/build-manifest.py here to verify resolution

New Theme

Themes are managed in the vauchi/themes repo. See the themes repo README for contributing instructions.

New Network

  1. Edit app-files-src/networks.json
  2. Validate and build as above

Landing-page copy variants

The opening shows one of nine versions of the copy (A–I). A random letter is kept in the current browser tab so that the copy stays the same while browsing. Static preview links select a specific version. Source copy lives in i18n/{lang}.json under hero.variant.{a-i}.*.

Choosing copy does not send click counts, time on page or other behavior reports. This applies to all visitors, including browsers without privacy signals. The old /beacon endpoint returns HTTP 410 for cached clients. The website image has no analytics collector and disables Nginx access logs.

The servers still receive connection details needed to serve a page, such as an IP address. Technical error logs can contain request details. The waitlist handles email addresses separately and keeps its own service logs. For example, signing up gives the waitlist your email even though viewing a copy variant sends no behavior report. See the privacy policy.

These statements describe this source version. Existing containers can retain older analytics files; an upgrade does not prove their erasure.

Waitlist

The landing page includes a self-hosted waitlist with double-opt-in confirmation emails. Confirmation links return a localized HTML result directly, without JavaScript, external assets, or an automatic redirect to the landing page. Both successful and invalid links work with scripts and styles disabled; the result is not cached and sends no referrer. The form posts to /waitlist/join; a small Flask app inside the container handles storage (SQLite) and sends confirmation mail via the existing Vauchi mailserver.

This optional email list is separate from Vauchi identities. It creates no app account. Both the site form and mail/removal pages support English, German, French, Italian and Spanish.

Configuration

Set these environment variables in the infra deployment (see vauchi/infra):

Variable Default Description
WAITLIST_DB_PATH ./waitlist.db SQLite database path
WAITLIST_REDIRECT_BASE_URL https://vauchi.app/ Base URL for redirect replies
WAITLIST_ENCRYPTION_KEY — Required base64-encoded 32-byte AES-256-GCM key
WAITLIST_SMTP_HOST mail.vauchi.app Outbound SMTP server
WAITLIST_SMTP_PORT 587 SMTP port
WAITLIST_SMTP_USER waitlist@vauchi.app SMTP auth user
WAITLIST_SMTP_PASSWORD — Required SMTP auth password
WAITLIST_SMTP_FROM waitlist@vauchi.app From address for confirmation emails
WAITLIST_SMTP_STARTTLS true Use STARTTLS
WAITLIST_ADMIN_API_KEY — Required API key for CSV export at /waitlist/admin/export
WAITLIST_PENDING_DAYS 7 Pending entry lifetime, 1–30 days from first submission
WAITLIST_CONFIRMED_DAYS 365 Confirmed entry lifetime, 1–365 days from confirmation
WAITLIST_CLEANUP_SECONDS 300 Supervised cleanup interval, 60–3600 seconds
WAITLIST_CONFIRMATION_SUBJECT localized Optional subject override for confirmation mail

Leaving and expiration

The confirmation email also contains a removal link. Existing subscribers can request one at /waitlist/manage. Opening the link displays a form; removal requires its explicit POST action, so a mail preview does not remove an entry. Invalid, expired and replayed removal links receive the same generic response. Requests for removal mail also receive the same response for known and unknown addresses; a valid address receives mail in either case.

Leaving removes the active email record, its lookup hash and its confirmation and removal capabilities. It cannot recall mail already being sent, or erase previous CSV exports, mail-server records or backups. The nightly backup includes the database and keeps 7 daily, 8 weekly and 12 monthly snapshots, so a removed address can stay in a backup for up to about a year. Operators must manage those copies separately. Restoring an old database can restore withdrawn entries; do not use a restored campaign for mailing without reconciling removals.

Expired entries are excluded from new exports. Each API request performs expiry, and the supervised cleanup process also runs without incoming requests. An outage can delay physical removal. Resending a pending confirmation does not extend its original expiry. Existing dated entries are subject to these periods on migration; the defaults are reviewable policy choices, not a readiness claim.

Admin export

Only confirmed, unexpired entries are exported. Keep any export only as long as needed to send the launch note; removal cannot update an exported copy.

curl -H "Authorization: Bearer $WAITLIST_ADMIN_API_KEY" \
  https://vauchi.app/waitlist/admin/export

An authenticated POST /waitlist/admin/close persistently closes the campaign and deletes its active entries. Repeating it is safe, and closure survives a service restart. New submissions then receive a closed response. It does not remove earlier exports or backups.

Security notes

  • Emails are encrypted at rest with AES-256-GCM. A deterministic SHA-256 hash is kept for duplicate detection; it can be guessed from a known email address. The operator holds the encryption key and can read the addresses. Management mail and the admin export decrypt them; this is not end-to-end encryption.
  • The waitlist API shares the container's unprivileged nginx runtime; the SQLite database is stored on a persistent volume owned by nginx.
  • Confirmation tokens are stored as SHA-256 hashes; only the emailed link contains the raw token. Reusable removal links are stored encrypted, with a hash for lookup, so a requested reminder can reuse the same link. A reminder does not invalidate an earlier removal link. Treat both link types as secrets.
  • A hidden honeypot field (name="website") catches naive bots without revealing itself.
  • The service does not write request audit or access logs. Delivery errors use fixed messages without addresses, IPs, tokens or SMTP exception text. Existing logs and mail-server records have a separate retention lifecycle. The outer TLS proxy has its own logging configuration; application settings do not disable that layer or delete historical records.
  • The admin export endpoint uses constant-time Bearer token comparison.
  • Nginx rate-limits /waitlist/join and /waitlist/manage to a shared allowance of 5 requests per minute per IP, with a burst of 3. The transient in-memory limiter is separate from durable request logging.

Related Repositories

Repository Description
vauchi/core Core Rust library
vauchi/android Android app
vauchi/ios iOS app
vauchi/docs Documentation
vauchi/assets Brand assets, logos
vauchi/infra Deployment configs

Support the Project

Vauchi is open source and community-funded — no VC money, no data harvesting.

License

MIT

About

Vauchi — Landing page (vauchi.app) (mirror of gitlab.com/vauchi)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages