Share one-time information securely. Create a short-lived text file, send a link, and it disappears forever after someone views it. 100% free, open-source, and self-hostable.
- One-time view: Content is automatically deleted after being viewed once
- End-to-end encryption: All entries are encrypted with AES-256-GCM before storage
- Optional TTL: Set a time-to-live for automatic expiration
- Anonymous: No account required, no tracking, no ads
- Self-hostable: Deploy your own instance with Docker in minutes
- Modern UI: Clean, minimal interface built with Next.js and Tailwind CSS
- High performance: Redis-based storage with sub-millisecond read/write times
- Structured logging: Production-ready logging with zerolog
- Go (1.25.4)
- Fiber (web framework)
- Redis (data storage)
- Zerolog (structured logging)
- Next.js 16
- React 19
- Tailwind CSS
- Shadcn UI
- Zod (validation)
The easiest way to self-host poof is using Docker Compose. This will set up both the API and Redis in minutes.
- Docker
- Docker Compose
- Bun (for web app) or Node.js
git clone https://github.com/dickeyy/poof.git
cd poofcd apps/apiCreate a .env file:
ENCRYPTION_KEY=your_64_character_hex_encryption_key_hereGenerate a secure encryption key:
openssl rand -hex 32Start the API and Redis:
docker-compose up -dThe API will be available at http://localhost:8080.
To view logs:
docker-compose logs -f apiTo stop the services:
docker-compose downcd ../webInstall dependencies:
bun install
# or
npm installCreate a .env.local file:
NEXT_PUBLIC_API_URL=http://localhost:8080Build and start the web app:
bun run build
bun run start
# or
npm run build
npm run startThe web app will be available at http://localhost:3000.
cd apps/apiMake sure Redis is running locally or via Docker:
docker run -d -p 6379:6379 redis:7-alpineCreate a .env file with your configuration:
APP_NAME=poof-api
PORT=8080
ENVIRONMENT=development
LOG_LEVEL=debug
REDIS_URL=redis://localhost:6379
ENCRYPTION_KEY=your_encryption_key_hereInstall dependencies:
go mod downloadRun the API:
go run main.go
# or
make runBuild the API:
make buildRun tests:
make testcd apps/webInstall dependencies:
bun installCreate a .env.local file:
NEXT_PUBLIC_API_URL=http://localhost:8080Start the development server:
bun run devThe web app will be available at http://localhost:3000.
Build for production:
bun run build| Variable | Description | Default |
|---|---|---|
APP_NAME |
Application name | poof-api |
PORT |
Server port | 8080 |
ENVIRONMENT |
Environment (development/production) | development |
READ_TIMEOUT |
Read timeout in seconds | 10 |
WRITE_TIMEOUT |
Write timeout in seconds | 10 |
IDLE_TIMEOUT |
Idle timeout in seconds | 120 |
LOG_LEVEL |
Log level (debug/info/warn/error/fatal/panic) | info |
REDIS_URL |
Redis connection URL | redis://localhost:6379 |
ENCRYPTION_KEY |
Required - AES-256 encryption key (64 hex chars) | - |
| Variable | Description | Default |
|---|---|---|
NEXT_PUBLIC_API_URL |
API base URL | - |
GET /health
Returns the health status of the API.
Response:
{
"status": "ok",
"service": "poof-api"
}POST /text
Create a new encrypted text entry.
Request Body:
{
"text": "Your secret message here",
"ttl": 3600 // Optional: Time-to-live in seconds
}Response:
{
"id": "AbCdEfGhIjKl"
}GET /text/:id
Retrieve and delete a text entry (one-time view).
Response:
{
"id": "AbCdEfGhIjKl",
"value": "Your secret message here"
}Note: After retrieval, the entry is immediately deleted from storage and cannot be accessed again.
You can create and retrieve poofs programmatically using the API. This is useful for integrating poof into scripts, CI/CD pipelines, or other applications.
# Create a poof
curl -X POST http://localhost:8080/text \
-H "Content-Type: application/json" \
-d '{"text": "Your secret message here"}'
# Response: {"id":"AbCdEfGhIjKl"}
# Retrieve the poof
curl http://localhost:8080/text/AbCdEfGhIjKl
# Response: {"id":"AbCdEfGhIjKl","value":"Your secret message here"}# Create a poof that expires in 1 hour (3600 seconds)
curl -X POST http://localhost:8080/text \
-H "Content-Type: application/json" \
-d '{"text": "This will expire in 1 hour", "ttl": 3600}'The API includes a multi-stage Dockerfile for optimized production builds.
cd apps/api
# Build the image
docker build -t poof-api .
# Run the container
docker run -d \
-p 8080:8080 \
-e ENCRYPTION_KEY=your_key_here \
-e REDIS_URL=redis://your-redis:6379 \
--name poof-api \
poof-apicd apps/api
docker-compose up -dThis will start both Redis and the API with proper networking.
cd apps/api
# Build the binary
CGO_ENABLED=0 go build -o poof-api main.go
# Run the binary
ENCRYPTION_KEY=your_key ./poof-apiThe easiest way to deploy the web app is with Vercel:
- Push your code to GitHub
- Import the project in Vercel
- Set the root directory to
apps/web - Add the environment variable:
NEXT_PUBLIC_API_URL - Deploy
Create a Dockerfile in apps/web:
FROM oven/bun:1 AS builder
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build
FROM oven/bun:1-slim
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["bun", "server.js"]cd apps/web
bun run buildDeploy the .next directory to any static hosting provider (Netlify, Cloudflare Pages, etc.).
poof/
├── apps/
│ ├── api/ # Go API
│ │ ├── internal/
│ │ │ ├── config/ # Configuration management
│ │ │ ├── crypto/ # AES-256 encryption/decryption
│ │ │ ├── handlers/ # HTTP handlers
│ │ │ ├── logger/ # Zerolog setup
│ │ │ ├── middleware/ # CORS, request ID, recovery
│ │ │ ├── redis/ # Redis client wrapper
│ │ │ └── router/ # Route definitions
│ │ ├── main.go
│ │ ├── Dockerfile
│ │ └── docker-compose.yml
│ └── web/ # Next.js web app
│ ├── app/ # App router pages
│ ├── components/ # React components
│ └── lib/ # Utility functions
├── LICENSE
└── README.md
- All text entries are encrypted using AES-256-GCM before being stored in Redis
- Encryption key must be provided via the
ENCRYPTION_KEYenvironment variable - Entries are automatically deleted after the first retrieval
- Optional TTL ensures entries expire even if never viewed
- No user data or tracking information is stored
- CORS is configured to allow cross-origin requests from web clients
- Password protection for entries
- Custom expiration times from the UI
- File upload support
- Syntax highlighting for code snippets
- API rate limiting
- Analytics dashboard (self-hosted)
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Go and Fiber
- UI powered by Next.js, Tailwind CSS, and Shadcn UI
- Inspired by services like Pastebin and PrivateBin
If you find this project useful, please consider sponsoring me.