DEV Community

Cover image for Wisp a Modern, Secure One-Time Secret Sharing App Built with Laravel 12, Vue 3 and Inertia.
Jerome Thayananthajothy
Jerome Thayananthajothy

Posted on • Edited on

Wisp a Modern, Secure One-Time Secret Sharing App Built with Laravel 12, Vue 3 and Inertia.

In a world where sensitive data leaks happen daily, sharing passwords, API keys, or confidential information over email or chat is risky.
We’ve all been there — “Can you send me the database password?” and before you know it, it’s stored permanently in Slack history or someone’s inbox.

That’s the problem Wisp solves.

The Solution: Wisp

Wisp is a secure, one-time secret sharing application.
It allows you to send encrypted secrets via unique links that expire after being viewed or after a set period of time, after which they are permanently deleted.

Key highlights:

  • Secrets are encrypted using AES-256-CBC before storage
  • Optional password protection
  • Links auto-expire after minutes, hours, or days
  • Secrets are deleted permanently after first viewing
  • No decrypted secret is ever stored server-side

Wisp New Secret Form

Technical Deep-Dive

Wisp is built with a stack that prioritizes security, developer productivity, and a modern user experience.

Backend

  • Laravel 12.21 with PHP 8.4
  • Laravel’s AES-256-CBC encryption
  • Bcrypt password hashing
  • MySQL 8.0+
  • PHPUnit for backend tests

Frontend

  • Vue 3.5 (Composition API) with TypeScript
  • Inertia.js 2.0 for seamless Laravel-Vue integration
  • Tailwind CSS 3.4 with shadcn/ui for consistent UI components
  • Ziggy for Laravel routes in JavaScript

Hosting

  • Deployed on Heroku with HTTPS enforced

Security Implementation

Wisp’s core principle: encrypt sensitive data immediately, destroy it quickly, and never store it in plain text.

How it works:

  1. Secrets are encrypted with AES-256-CBC before saving to the database.
  2. If a password is set, it is hashed using Bcrypt and verified before decryption.
  3. On first view, the secret is permanently deleted.
  4. If not viewed, it is automatically removed at the set expiration time.
// Laravel encryption example
$encrypted = encrypt($secretContent);
$decrypted = decrypt($encrypted);
Enter fullscreen mode Exit fullscreen mode

Wisp Password Protected View

Why Inertia.js?

Instead of building a traditional API with a separate frontend, Wisp uses Inertia.js to connect Laravel and Vue directly.

Benefits:

  • No need for API controllers or serialization layers
  • Laravel handles routing and authentication natively
  • SPA experience without managing a separate API stack

Example Inertia route in Laravel:

Route::get('/secrets/create', function () {
    return Inertia::render('Secrets/Create');
})->name('secrets.create');
Enter fullscreen mode Exit fullscreen mode

Wisp Secret View with Deletion Notice

Developer Experience

  • Laravel Pint for automatic code formatting
  • Nightwatch.js for end-to-end testing of the secret sharing flow
  • Full TypeScript support for frontend predictability
  • shadcn/ui for accessible and reusable components

Installation

Clone the repository:

git clone https://github.com/Thavarshan/wisp.git
cd wisp
Enter fullscreen mode Exit fullscreen mode

Install dependencies:

composer install
npm install
Enter fullscreen mode Exit fullscreen mode

Configure environment:

cp .env.example .env
php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

Run migrations:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Start the application:

php artisan serve
npm run dev
Enter fullscreen mode Exit fullscreen mode

Usage

  1. Visit / and create a new secret
  2. Set an optional password and expiration time
  3. Share the generated link
  4. Once opened, the secret is permanently deleted

Wisp Secret View with Deletion Notice

Contributing

Wisp is open-source and welcomes contributions.

Call to Action

If you’ve ever sent a password over chat and worried about where it might end up, Wisp is for you.
Try it out, give it a star on GitHub, and help improve it. Together, we can make secure sharing the default.

Top comments (1)

Collapse
 
xwero profile image
Info Comment hidden by post author - thread only accessible via permalink
david duymelinck • Edited

I'm wondering the choice for vue, inertia, tailwind and shadcn/ui when looking at the images. Is that why you call it modern?
A javascript frontend doesn't make sense from a security viewpoint. It is an extra layer that needs protection.

I wouldn't make the password optional. The secret url is only protected by obscurity. While throttling helps with brute force attacks. Bots can keep trying without stopping, so it could be possible some urls could be guessed.

I would even add a recipient, having two things to guess makes it harder to find the right combination.

Making security software is a hard nut to crack, because most of the times as developers we focus on the happy path and consider edge cases.
But for security software you need to focus on the unhappy path and still make the solution work with that balance for the end users between security and ease of use.

Some comments have been hidden by the post's author - find out more