Simple, Docker-based password generator and one-time secret sharing service.
Demo: https://password.ntcu.be/
- AES-256-GCM encryption
- Redis-backed storage with TTL (persistence optional, keeping secrets after restart of Redis container)
- One-time secrets (optional)
- Nginx + app rate limiting
- Custom branding via
.env
- Docker
- Docker Compose v2+
Generate a key:
openssl rand -hex 32- Create
.env(required for both options):
cp .env.example .envSet at least:
ENCRYPTION_KEY=<32 byte hex>
PUBLIC_BASE_URL=http://localhost- Pull or Build the App's Image:
Choose one of the options below.
Create a folder anywhere and add two files: .env and docker-compose.yml.
Choose one of these compose examples:
services:
nginx:
image: waffle047/password-generator-nginx
container_name: password-generator_nginx
ports:
- "80:80"
depends_on:
- app
networks:
- public
- internal
app:
image: waffle047/password-generator
container_name: password-generator_app
env_file:
- .env
environment:
NODE_ENV: production
REDIS_URL: redis://redis:6379
depends_on:
- redis
networks:
- internal
redis:
image: redis:7-alpine
container_name: password-generator_redis
command: ["redis-server", "--appendonly", "yes", "--save", "60", "1"]
volumes:
- redis-data:/data
networks:
- internal
networks:
public:
driver: bridge
internal:
internal: true
volumes:
redis-data:services:
nginx:
image: waffle047/password-generator-nginx
container_name: password-generator_nginx
ports:
- "80:80"
depends_on:
- app
networks:
- public
- internal
app:
image: waffle047/password-generator
container_name: password-generator_app
env_file:
- .env
environment:
NODE_ENV: production
REDIS_URL: redis://redis:6379
depends_on:
- redis
networks:
- internal
redis:
image: redis:7-alpine
container_name: password-generator_redis
command: ["redis-server", "--save", "", "--appendonly", "no"]
networks:
- internal
networks:
public:
driver: bridge
internal:
internal: trueRun:
docker compose up -dOpen:
git clone https://github.com/insert-waffle/password-generator.git
cd password-generator
cp .env.example .env
docker compose up -d --buildRequired:
ENCRYPTION_KEY=<32 byte hex>Optional:
PUBLIC_BASE_URL=https://yourdomain.com
BRAND_PRIMARY_COLOR=#000000
BRAND_LOGO_URL=https://...
BRAND_FAVICON_URL=https://...
BRAND_TITLE=Your Brand
BRAND_TAGLINE=Secure sharing
BRAND_SITE_TITLE=Your AppBase URL: http://<host>
Create a new secret.
Headers:
Content-Type: application/json
Body:
{
"password": "string",
"expirySeconds": 86400,
"oneTime": false,
"viewsLimit": 3
}Fields:
password(string, required) — max 4096 chars.expirySeconds(integer, required) — must be > 0 and <=MAX_EXPIRY_SECONDS(default 2592000).oneTime(boolean, optional) — delete after first successful read.viewsLimit(integer, optional) — if provided, allowed views (1–50). When set, expiry is forced toMAX_EXPIRY_SECONDS.
Response (201):
{ "id": "uuid-v4" }Errors:
- 400 — missing/invalid fields, password too long, invalid
expirySecondsorviewsLimit - 413 — payload too large (body limit 1mb)
- 415 — wrong content type
- 429 — rate limited
Retrieve and decrypt a secret.
Response (200):
{
"password": "decrypted password",
"oneTime": false,
"expiresAt": 1771680459214,
"remainingViews": 2
}Notes:
expiresAtis included for non-one-time secrets when known.remainingViewsis included whenviewsLimitwas set.
Errors:
- 404 — not found, expired, deleted, or invalid UUID
- 500 — corrupted or undecryptable payload
Returns public configuration and branding data used by the UI.
Response (200):
{
"publicBaseUrl": "https://example.com",
"version": "0.1.0",
"branding": {
"primaryColor": "#cc2936",
"logoUrl": "https://...",
"faviconUrl": "https://...",
"title": "...",
"tagline": "...",
"siteTitle": "..."
}
}